分享
 
 
 

C++ FAQ Lite[23]--继承(你所不知道的)(更新)

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

[23] 继承 — 你所不知道的

(Part of C++ FAQ Lite, ]Copyright © 1991-2001, Marshall Cline, cline@parashift.com)

简体中文版翻译:申旻nicrosoft@sunistudio.com东日制作室东日文档

FAQs in section [23]:

][23.1] 基类的非虚函数调用虚函数可以吗?

][23.2] 上面那个FAQ让我糊涂了。那是使用虚函数的另一种策略吗?

][23.3] 当基类构造函数调用虚函数时,为什么不调用派生类重写的该虚函数?

][23.4] 派生类可以重置(“覆盖”)基类的非虚函数吗?

][23.5] “Warning: Derived::f(float) hides Base::f(int)”是什么意思?

][23.6] "virtual table" is an unresolved external 是什么意思?

[23.1] 基类的非虚函数调用虚函数可以吗?

[Recently created (on 4/01). ]Click here to go to the next FAQ in the "chain" of recent changes.]

可以。有时(并非总是!)这是一个好主意。例如,假设所有Shape(图形)对象有一个公共的打印算法。但这个算法依赖于它们的面积并且它们都有不同的方法来计算面积。在这种情况下,Shape的area()方法(译注:得到Shape面积的成员函数)必须是virtual的(可能是纯虚(pure-virtual)的),但Shape::print()可以在Shape中被定义为非虚(non-virtual)的,]前提是所有派生类不会需要不同的打印算法

#include "Shape.hpp"

void Shape::print() const

{

float a = this->area(); // area() 为纯虚

// ...

}

[ Top | Bottom | Previous section | Next section ]

[23.2] 上面那个FAQ让我糊涂了。那是使用虚函数的另一种策略吗?

[Recently created (on 4/01). ]Click here to go to the next FAQ in the "chain" of recent changes.]

是的,那是不同的策略。是的,那的确是使用虚函数的两种不同的基本方法:

假设你遇到了上一个FAQ所描述的情况:每一个派生类都有一个结构完全一样,只有一小块不同的方法。因此算法是相同的,但实质不相同。在这种情况下,你最好在基类写一个全面的算法作为public:方法(有时是非虚的),然后在派生类中写那不同的一小块。这一小块在基类中声明(通常是protected:的,纯虚的,当然至少是virtual的),并且最终在每个派生类中被定义。这种情况下最紧要的问题是包含全面的算法的public:方法是否应该是virtual的。答案是,]如果你认为某些派生类可能需要覆盖它,就让它成为virtual的。

假设你遇到了上一个FAQ完全相反的情况,每一个派生类都有一个结构完全不同,但有一小块的大多数(如果不是全部的话)相同的方法。在这种情况下,你最好将全面的算法放在最终在派生类中定义的public: virtual之中,并且将一小块可以被只写一次的公共代码(避免代码重复)隐藏在某处(任何地方!)。一般放在基类的protected:部分,但不是必须的,也可能不是最好的。找个地方隐藏它们就行了。注意,由于public:用户不需要/不想做它们做的事情,如果在基类中隐藏它们,通常应该使它们是protected:的。假定它们是protected:的,那么可能不应该是virtual的:如果派生类不喜欢它们之一的行为,可以不必调用这个方法。

强调一下,以上列表中的是“既/又”情况,而不是“二者选一”的。换句话说,在任何给定的类上,不必在两种策略中选择。既有一个符合策略 #1 的方法f(),又有一个符合策略 #2 的方法g()是非常正常的。换句话说,在同一个类中,有两种策略同时工作是非常正常的。

[ Top | Bottom | Previous section | Next section ]

[23.3] 当基类构造函数调用虚函数时,为什么不调用派生类重写的该虚函数?

当基类被构造时,对象还不是一个派生类的对象,所以如果 Base::Base()调用了虚函数 virt(),则 Base::virt() 将被调用,即使 Derived::virt()(译注:即派生类重写的虚函数)存在。

同样,当基类被析构时,对象已经不再是一个派生类对象了,所以如果 Base::~Base()调用了virt(),则 Base::virt()得到控制权,而不是重写的 Derived::virt() 。

当你可以想象到如果 Derived::virt() 涉及到派生类的某个成员对象将造成的灾难的时候,你很快就能看到这种方法的明智。详细来说,如果 Base::Base()调用了虚函数 virt(),这个规则使得 Base::virt()被调用。如果不按照这个规则,Derived::virt()将在派生对象的派生部分被构造之前被调用,此时属于派生对象的派生部分的某个成员对象还没有被构造,而 Derived::virt()却能够访问它。这将是灾难。

[ Top | Bottom | Previous section | Next section ]

[23.4] 派生类可以重置(“覆盖”)基类的非虚函数吗?

合法但不合理。

有经验的 C++ 程序员有时会重新定义非虚函数(例如,派生类的实现可能可以更有效地利用派生类的资源),或者为了回避]隐藏规则。即使非虚函数的指派基于指针/引用的静态类型而不是指针/引用所指对象的动态类型,但其客户可见性必须是一致的。

[ Top | Bottom | Previous section | Next section ]

[23.5] “Warning: Derived::f(float) hides Base::f(int)” 是什么意思?

意思是:你要完蛋了。

你所处的困境是:如果基类声明了一个成员函数f(int),并且派生类声明了一个成员函数 f(float)(名称相同,但参数类型和/或数量不同),那么 Base 的 f(int)被隐藏(hidden)而不是被重载(overloaded)或被重写(overridden)(即使 基类的f(int)是虚拟的)

以下是你如何摆脱困境:派生类必须有一个被隐藏成员函数的using 声明,例如:

class Base {

public:

void f(int);

};

class Derived : public Base {

public:

using Base::f; // This un-hides Base::f(int)

void f(double);

};

如果你的编译器不支持using语法,那么就重新定义基类的被隐藏的成员函数,]即使它们是非虚的。一般来说这种重定义只不过使用::语法调用了基类被隐藏的成员函数,如,

class Derived : public Base {

public:

void f(double);

void f(int i) { Base::f(i); } // The redefinition merely calls Base::f(int)

};

[ Top | Bottom | Previous section | Next section ]

[23.6] "virtual table" is an unresolved external 是什么意思?

如果你得到一个连接错误"Error: Unresolved or undefined symbols detected: virtual table for class Fred",那么可能是你在 Fred 类中有一个未定义的成员函数。

编译器通常会为含有虚函数的类创建一个称为“虚函数表”的不可思议的数据结构(这就是它如何处理]动态绑定的)。通常你根本不必知道它。但如果你忘了为Fred 类定义一个虚函数,则有时会得到这个连接错误。

许多编译器将这个不可思议的“虚函数表”放进定义类的第一个非内联虚函数的编辑单元中。因此如果 Fred 类的第一个非内联虚函数是 wilma(),那么编译器会将 Fred 的虚函数表放在 Fred::wilma() 所在的编辑单元里。不幸的是如果你意外的忘了定义 Fred::wilma(),那么你会得到一个"Fred's virtual table is undefined"(Fred的虚函数表未定义)的错误而不是“Fred::wilma() is undefined”(Fred::wilma()未定义)。

[ Top | Bottom | Previous section | Next section ]

E-mail the author

[ C++ FAQ Lite | Table of contents | Subject index | ]About the author | | ]Download your own copy ]

Revised Apr 8, 2001

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有