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

        当多重继承(multiple inheritance;MI)进入设计景框,程序有可能从一个以上的base classes继承相同名称(如函数、typedef等等)。那会导致较多的歧义机会。例如:

class BorrowableItem {             // 图书馆允许你借某些东西
public:
	void checkOut();               // 离开时进行检查
	...
};
class ElectronicGadget {
private:
	bool checkOut() const;        // 指向自我检测,返回是否测试成功
	...
};
class MP3Player:                 // 注意这里的多重继承(某些图书馆愿意借出MP3播放器)
    public BorrowableItem, 
    public ElectronicGadget 
{
	...                          // 这里,class的定义不是我们关心重点 
};
MP3Player mp;
mp.checkOut();                   // 歧义!调用的是哪个checkOut?

        注意此例之中对checkOut的调用是歧义(模棱两可)的,即使两个函数之中只有一个可取用(BorrowableItem内的checkOut是public,ElectronicGadget内的却是private)。这与C++用来解析(resolving)重载函数的调用的规则相符:在看到是否有个函数可取用之前,C++首先确认这个函数对此调用之言是最佳匹配。找出最佳匹配函数后才检验其可取用性。本例的两个checkOuts有相同的匹配程度(译注:因此才造成歧义),没有所谓最佳匹配。因此ElectronicGadget::checkOut的可取用性也就从未被编译器审查。

        为了解决这个歧义,你必须明白指出你要调用哪一个base class内的函数:

mp.BorrowableItem::checkOut();    // 哎呀,原来是这个checkOut...

        你当然也可以尝试明确调用ElectronicGadget::checkOut,但然后你会获得一个“尝试调用private成员函数”的错误。

        多重继承的意思是继承一个以上的base classes,但这些base classes并不常在继承体系中又有更高级的base classes,因为那会导致要命的“钻石型多重继承”:

        任何时候如果你有一个继承体系而其中某个base class和某个derived class之间有一条以上的相通路线(就像上述的File和IOFile之间有两条路径,分别穿越InputFile和OutputFile),你就必须面对这样一个问题:是否打算让base class内的成员变量经由每一条路径被复制?假设File class有个成员变量fileName,那么IOFile内该有多少笔这个名称的数据呢?从某个角度说,IOFile从其每一个base class继承一份,所以其对象内应该有两份fileName成员变量。但从另一个角度说,简单的逻辑告诉我们,IOFile对象只该有一个文件名称,所以它继承自两个base classes而来的fileName不该重复。

        C++其缺省做法是执行复制(也就是上一段所说的第一个做法)。如果那不是你要的,你必须令那个带有此数据的class(也就是File)成为一个virtual base class。为了这样做,你必须令所有直接继承自它的classes采用“virtual继承”:

        C++标准程序库内含一个多重继承体系,其结构就如右图那样,只不过其classes其实是class templates,名称分别是basic_ios,basic_istream,baseic_ostream和basic_iostream,而非这里的File,InputFile,OutputFile和IOFile。

        virtual继承的classes所产生的对象往往比使用non-virtual继承的体积大,访问virtual base classes的成员变量时,也比访问non-virtual base classes的成员变量速度慢。种种细节因编译器不同而异,但基本重点很清楚:你得为virtual继承付出代价。

        virtual继承的成本还包括其他方面。支配“virtual base classes初始化”的规则比起non-virtual bases的情况远为复杂且不直观。

        我对virtual base classes(亦相当于对virtual继承)的忠告很简单。第一,非必要不使用virtual bases。平常请使用non-virtual继承。第二,如果你必须使用virtual base classes,尽可能避免在其中放置数据。这么一来你就不需担心这些classes身上的初始化(和赋值)所带来的诡异事情了。Java和.NET的Interfaces值得注意,它在许多方面兼容于C++的virtual base classes,而且也不允许含有任何数据。

请记住

  • 多重继承比单一继承复杂。它可能导致新的歧义性,以及对virtual继承的需要。
  • virtual继承会增加大小、速度、初始化(及赋值)复杂度等等成本。如果virtual base classes不带任何数据,将是最具有实用价值的情况。
  • 多重继承的确有正当用途。其中一个情节涉及“public继承某个Interface class”和“private继承某个协助实现的class”的两相组合。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值