/*此文是译者出于自娱翻译的GotW(Guru of the Week:http://www.gotw.ca/gotw/index.htm)系列文章的一篇,原文的版权是属于Hub Sutter(著名的C++专家,《Exceptional C++》的作者)。此文的翻译没有征得原作者的同意,只供学习讨论。——译者:黄森堂*/
#37 多重继承 Ⅰ.
难度:6/10
在很多的语言里,都包含了SQL3标准,并都继续讨论决定是否支持单一或多个继承,本期GotW将引发你思考这个问题。
JG问题:
1.什么是多重继承(MI),在C++中引入MI是否需要额外或其它因素呢?
Guru问题
2.MI是必需的吗?,如果是,给出在多少不同的情形里你需要它,且为什么它会放在语言里;如果非必需,为什么SI是相等或更高级的(或者结合java的接口类型),且为什么MI不会放在语言里。
[注解:本期Gotw不是有意挑起争端,这儿现在只讨论MI,然而,因为其它流行语言也有相同的特征:在Porland(波特兰大)以前的两周里,ANSI SQL3委员们表决是否在标准OO/ORDBMS里移走MI特征,在最后一个月里委员们在Australia(澳大利亚)召开会议,如果它们的观点相同的话,在ANSI会议上将表决通过,那么SQL3只包含SI(不像java类型接口的妥协).-hps]
解决方法:
1.什么是多重继承(MI),在C++中引入MI是否需要额外或其它因素呢?
简要地告诉你:
MI的方法使用得有能力从多个单个基类中进行继承,例如:
class Derived : public Base1, private Base2
{
//...
};
允许引入MI是可能有的类需要在多个父类中有一个相同的基类,这是一个简单的示例图:
B
/ C1 C2
\ /
D
这儿的B是D的两个间接基类,通过一个C1与C2.
这种情形就需要额外的特征在C++里:虚继承(virtual inheritance),当程序员需要使用类D是有一个B子对象,还是两个呢?,如果是一个,B将是D的虚基类,如果是两个,B将是D的正常基类(非虚基类).
最后,虚基类主要的复杂性是它们在大多数的派生类里必须使用直接初始化,更多的信息请参阅其它方面的MI知识。请留心非常好的书,像《C++ Programming》3/e与Meyers的《Effective C++》
2.MI是必需的吗?
简要回答:没有这种特征的话,严格来说在一定范围有些程序员可能需要使用到汇编,然而,更多的人宁可在C的程序代码里不使用虚函数,在很多的情形里,没有MI是很痛苦的。
如果是,给出在多少不同的情形里你需要它,且为什么它会放在语言里;如果非必需,为什么SI是相等或更高级的(或者结合java的接口类型),且为什么MI不会放在语言里。
简要的回答:是与不是.
像任何工具一样,MI在小心使用,使用MI总是增加复杂性,但它们有共同的特征:与可供选择的办法之比较,它还是简单与可维护性。应该像这样问:“你很少只需要MI,但当你需要它的时候你真的需要它吗?”
它们有许多特征是很方便与有用的工具。我仅仅说明三点(在实际上,许多方面包含在以下三类里):
1.接口类(抽象纯基类)
在C++里,MI使用是非常的安全,使用它定义接口类,但类的组成是纯虚函数,在细节里,在基类里它没有数据成员,为此来避免MI的复杂性。
有趣的是,不同的语言/模型支持这种MI是通过没有继承机制来实现,用JAVA与COM作例子:Java只支持SI,但支持概念上的类实现多个接口对于C++来说是相当的简单,它只需要使用纯虚函数;COM不包含继承,但同样也有概念上的接口组合,这种模型简单地组合了Java的接口与C++的模板(template)
2.组合模块/库
许多类都设计了基类,你中要从它们那里继承就可以使用它们;如果你想写个类来扩展两个库,你需要从哪一个类继承吗?,因为你通常不会选择去改变库代码(如果你是从第三方采购而来的库而没有源程序的时候),MI是必须的。
3.安心使用(多态)
这儿的示例是:哪儿允许MI在不同的情形里使用相同的多态对象呢?一个较好的示例是在《C++ Programming Language 3/e》14.2.2,它示范了如何使用MI来设计异常类,那儿有许多的派生异常类可以使用IS-A关系的多态与从多个基类中派生。
注解:#1与#3是交叉的.
最后,不要忘记“polymorphic LSP IS-A public inheritance”,它不是在小镇是玩游戏;那儿有需要其它使用继承的理由,结果是:有时它不仅仅需要从多个基类中派生,还需要从每一个不同的前提来考虑,例如:类可能需要从基类A进行private继承来获得存取A的protected成员,但在同一时间,从基类B进行public继承来重载B的虚函数