C++之多重继承

C++中,所谓的多继承是指一个派生类可以有多个基类,这样就可能带来以下几方面的问题:

(1)多个基类中存在相同名称时

如果一个派生类继承的多个基类中包含有相同名称的函数时,有可能会产生调用不明确,即发生歧义,比如如下代码:

 1 class BorrowableItem
 2 {
 3 public:
 4 void checkOut();
 5 };
 6 
 7 class ElectronicGadget
 8 {
 9 public:
10 bool checkOut() const;
11 };
12 
13 class MP3Player : public BorrowableItem, public ElectronicGadget{ ... };
14 
15 MP3Player mp3;
16 mp3.checkOut();
View Code

上述代码中对象mp3调用函数checkOut时就会产生歧义,因为它的基类中都含有函数checkOut。编译器在查找可用的函数时,首先查找最佳匹配函数,然后再检查其是否可用,上面的两个基类的函数具有相同的匹配度,没有所谓的最佳匹配,因而编译器也不会检查可取用性。

为了指明到底是调用哪个基类的函数,通常需要如下指定:

mp3.BorrowableItem.checkOut();

(2)钻石继承体系
如果一个派生类继承的多个基类中,向上还要继承其他的类,例如

class File { };
class InputFile : public File { ... };
class OutputFile : public File { ... };
class IOFile : public InputFile, public OutputFile { ... };

这种形式的继承像极了钻石,因而也称钻石继承:

这种继承带来了这样的问题:如果一个继承体系中从一个base class到一个derived class有一个以上的通路,那么该让base class的成员变量在每一个通路中的derived class中都复制一次吗?如果是,那么这种复制必然会带来不必要的空间开销, 例如在上面的继承体系中,如果File中包含有一个fileName变量,那么IOFile类中就会包含两份fileName变量的空间,这在现实中显然是不合理的。

对此,C++提供两种方案:第一种缺省做法是提供复制,即上面的做法;第二是对基类进行virtual继承,形式如下:

1 class File { ... };
2 class InputFile : virtual public File { ... };
3 class OutputFile : virtual public File { ... };
4 class IOFile : public InputFile, public OutputFile { ... };
View Code

这种改变理论上来讲总是合理的,即public继承应当为virtual public继承,但是之所以不这么做的原因是,为了处理成员变量重复的问题,编译器需要做很多复杂的事情,对应的产生的派生类对象的体积也更大,访问virtual base class成员变量的速度也更慢。

此外,对virtual base class的初始化也更加复杂,其初始化责任由最底层的派生类承担(一般的继承中不是如此吗?)。

基于以上,可以看到,如果必须使用virtual base class,那么最好在virtual base class中少放数据,以避免初始化时可能带来的诡异行为。

(3)多重继承的合理性

如果一个类的许多实现可以通过使用已有的类的实现达到便利,即"is-implemented-in-terms-of",那么可能需要private继承,当然这里也可以使用复合加public继承,假如这里执意用private继承。另一方面,该类还需要实现某个接口类,即必须通过public继承某个包含许多接口的类,那么多重继承看来就是合理的:

 1 class IPerson
 2 {
 3 public:
 4 virtual ~IPerson();
 5 virtual string name() const = 0;
 6 virtual string birthDate() const = 0;
 7 };
 8 
 9 class DatabaseID { ... };
10 class PersonInfo
11 {
12 public:
13 explicit PersonInfo(DatabaseID pid);
14 virtual ~PersonInfo();
15 virtual const char* theName() const;
16 virtual const char* theBirthDate() const;
17 virtual const char* valueDelimOpen() const;
18 virtual const char* valueDelimClose() const;
19 ...
20 };
21 
22 class CPerson : public IPerson, private PersonInfo
23 {
24 public:
25 explicit CPerson(DatabaseID pid) : PersonInfo(pid) { }
26 virtual string name() const
27 {
28 return PersonInfo::theName();
29 }
30 virtual string birthDate() const
31 {
32 return PeronInfo::theBirthDate();
33 }
34 
35 private:
36 const char* valueDelimOpen() const { return ""; }
37 cpnst char* valueDelimClose() const { return ""; }
38 };
View Code

 

总结来讲,多重继承会产生歧义性,可能需要引入virtual base class,而virtual base class又可能造成大小、速度、初始化复杂度增加的成本,因而如果能避免使用多重继承,就应该避免。在某写场合下,比如需要public继承一个借口类和private继承一个协助实现的类时,采用多重继承会带来便利。

 

以上整理自Effective C++中文版第三版case 40.

posted on 2013-06-05 21:08  Sophia-呵呵小猪  阅读(280)  评论(0编辑  收藏  举报