前言
一旦谈及multiple inheritance(MI),C++程序员总会分为两个阵营:
- single inheritance(SI)不错,MI更好。
- SI不错,MI垃圾
多重继承带来的歧义
抛开争论,首先我们必须要认清:只要我们在设计中使用了MI,程度便有可能从一个以上的classes继承相同名称,这会引发一些歧义,举例而言:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class BorrowableItem{//允许借阅物
public:
void checkOut();//离开时检查
...
};
class ElectronicGadget{//电子设备
private:
bool checkOut() const;//执行自我检测
};
class MP3player://Mp3是借阅物也是电子设备
public BorrowableItem,
public ElectronicGadget
{...};
MP3Player mp;
mp.checkOut();//歧义产生,改掉用哪个?
为了避免这些歧义,我们应该使用作用域运算符::来指明具体调用哪个版本。例如:1
mp.BorrowableItem::checkout();
“钻石型”多重继承
多重继承是指继承一个以上的base classes,但这些base class一般不作为另一些class 的derived class,因为那会导致”钻石型”多重继承。
问题实例
1
2
3
4
5
6
7class File {...};
class InputFile:public File {...};
class OutputFile:public File {...};
class IOFile:
public InputFile,
public OutputFile
{...};
具体到这个实例,有问题如下:假定File内有一个名为FileName的成员变量,那么IOFile对象内应该有几个此成员呢?它可能会忠实地完全复制从InputFile和OutputFile继承而来的所有成员,从而导致存在两个FileName,也可以只继承其中的一个。
virtual base class 与 virtual继承
C++对上述的两种方案都支持,但默认执行完全复制。如果这并非你所愿,那你必须令带有重复数据的class(也就是File)成为一个virtual base class,同时,你还必须令所有直接继承它(file)的class采用virtual继承。实例如下:1
2
3
4
5
6
7class File {...};
class InputFile:virtual public File {...};
class OutputFile:virtual public File {...};
class IOFile:
public InputFile,
public OutputFile
{...};
STL中就有一个结构类似的多重继承体系,只不过其classes是class templates,名称分别是basic_ios,basic_istream,basic_ostream,basic_iostream。
virtual继承的成本(详见More Effective C++ 24)
从行为正确的观点看,public继承应该总是virtual。但是实际上,为了避免继承得来的变量重复,编译器不得不使用一些其它操作(因此付出了成本),使用virtual继承的class所产生的对象一般比用non-virtual继承产生的对象要大,而且访问速度也慢。
virtual继承的成本还体现在其他方面,比如说virtual base classes初始化规则比起non-virtual bases的情况远为复杂而去不直观。virtual base class的初始化责任是由继承体系中的最底层class负责,这意味着:
- classes若派生自virtual base class而需要初始化,则其必须认知virtual base—无论他们离的多远。
- 当一个新的dc加入继承体系,它必须承担起其virtual base的初始化责任。
对于virtual base(或者说virtual继承),我们应该保证非必要不使用。如果必须使用,则尽力避免在virtual base中放置任何数据,如此则不必担心初始化等等问题。(这不得不让人想起了Java的Interface,它在相当多的情况下兼容于C++中的virtual base class)。
Interface class(详见Effective C++ 32)
问题实例
(课本中接着描述了Person interface与具体实现的class)
如果我们需要用某个类的现有接口,又希望使用另一个类现有的具体实现,无疑,我们可以public from interface && private from implement。
总结
- 多重继承比单一继承复杂,并且有可能导致歧义性,以及对virtual继承的需要。
- virtual继承会增加大小,速度,初始化各方面的成本,但如果virtual base不带有任何数据,其使用颇有价值。
- 多重继承确有正当用途,比如public继承interface且private继承implement class.