条款40:明智而审慎地使用多重继承

来源:互联网 发布:《人生》知乎 编辑:程序博客网 时间:2024/05/23 11:01

多重继承往往会导致二义性:

class Base1{public:void func(){cout<<"base1 func"<<endl;}};class Base2{private:bool func(){cout<<"base2 func"<<endl;return true;}};class Derived:public Base1,public Base2{};int main(){Derived d;//d.func();二义性return 0;}
也许你会奇怪,Base2中的func应该是不会被继承下来的,可是为什么在调用func时还会产生二义性呢?这是因为编译器在检查函数是否可取之前,先会判断这个函数是否是最佳匹配。此时,有两个最佳匹配,所以就会出现二义性。所以,最好是明确你要调用的是哪个基类的函数:d.Base1::func();


这还不是最可怕的情况,最可怕的是出现“钻石型多重继承”:B和C继承自A,而且D继承自B和C。此时,理论上讲,D中会有两份A的public成员(这里假定是public继承),但实际上,大多数时候,我们只希望有一份,此时只能通过虚继承来避免这种现象。
class Base{public:void print(){cout<<"base"<<endl;}};class Middle1:public Base{};class Middle2:public Base{};class Derived:public Middle1,public Middle2{};
此时,d.print();将会有二义性,因为他不知道自己调用的是从Middle1继承下来的print还是从Middle2继承下来的print。此时,只需要将Middle1和Middle2声明为虚继承,就会只从基类中保存一份print副本。
我们觉得,似乎所有的继承都该被声明为虚继承,但是虚继承会使派生类的大小变得更大,而且访问速度更慢。还有一点需要注意的,就是在使用虚继承之后,构造函数的调用顺序也变了:不再是派生类调用自己的基类,依次向上层调用了,而是从最高的基类开始,向下调用。
因此,不到万不得以,不要使用虚基类,在使用时,尽量避免在其中放置数据,因为它的初始化规则跟一般情况不同。


然后书中给出了一个使用的多重继承的例子:有一个接口类,我们需要实现它;恰巧我们有一个现成的类,这个类的功能跟我们要实现的类相似,但是我们需要对它的虚函数进行重写,已达到我们希望的功能,于是我们public继承了这接口,又private继承了它的实现,并对其中的某些实现重定义了。这时候,多重继承真的就派上了用场。
总的来说,多重继承可能会引起很多问题:二义性、构造、析构函数的顺序,类的大小、访问速度等等。但有时的确也会使用到它。

原创粉丝点击