1、多种继承,首先带来一个问题。那就是子类会从多个父类中继承相同的名称。这样,在调用的时候,必然导致歧义。
2、考虑Base1中有个public方法XXX,Base2中有个private方法XXX,Derived继承Base1和Base2,那么分析一下,Derived d; d.XXX()到底调用哪个方法?
你可能会想,虽然两个都匹配,但是只有Base1的XXX方法可访问,因此调用Base1的XXX方法。这种想法是错的,编译报错:对XXX的访问不明确。为什么?
因为编译器决议调用哪个方法的时候,第一步,先把所有匹配的名称都拿来,不管是不是可以访问,然后在里面找到一个最优匹配,如果两个匹配程度一样,编译器就傻眼了,只能报错。经过测试,从多个父类继承相同的名称,即使匹配程度不一样,也会导致对XXX的访问不明确。
怎么解决这个问题呢?
明确告诉编译器,我要调用哪个父类的方法。
d.Base1::XXX(); //OKd.Base2::XXX(); //Error,无法访问成员XXX
注:单一继承也可以使用d.Base1::XXX();这种形式。
3、对于菱形继承(前面的文章有详细的介绍),考虑InputFile继承File,OutputFile继承File,IOFile继承InputFile和OutputFile,File中有个数据fileName,现在出问题了。从继承角度讲,IOFile有两份fileName,但根据常识,IOFile应该只有一个fileName。C++默认的做法是IOFile中有两个fileName。
4、如何解决上面的问题呢?也就是说,保证IOFile中只有一个fileName。通过使用virtual继承。让InputFile和OutputFile使用virtual继承,IOFile的继承方式不变。这样就使得IOFile中,只有一个fileName。显而易见,这种做法非常丑陋。而且,虽然起到了效果,要付出很大的代价。编译器在幕后做了一些事情,导致的结果是:InputFile的体积变大,访问变慢,初始化也可能带来诡异的问题。
5、因此,尽量少使用virtual继承。如果必须使用virtual继承,也要保证不在virtual Base(即File)中放置数据,从而保证IOFile中没有重复的数据。C#和Java中的interface就不允许有数据。相对而言,有重复的方法,还好办,可以指定调用哪个父类的方法。
6、那么,多重继承就一无是处吗?多重继承的使用场景:public继承一个类,暴露接口,同时private继承一个类,根据它来实现。你可能会想,根据某物实现出,不是应该优先使用复合技术吗?private继承可以解决两个问题:一是private继承可以访问protected成员,二是可以重写方法。