读书笔记:Effective C++ 炒冷饭 - Item 40 小心使用多重继承

读书笔记:Effective C++ 炒冷饭 - Item 40 小心使用多重继承

[原创文章欢迎转载,但请保留作者信息]

Justin 于 2010-01-21

Scott 开篇就直接说开了: C++ 阵营中关于多重继承 (Multiple Inheritance, MI) 分成了两派,一派认为多重继承比单一继承好,另外一边则认为弊大于利。

所以本课的内容就是说说 MI 的优与劣。

MI 的第一个问题就是名字冲突, 最经典的例子就是钻石问题 (diamond problem)。
设想 A 中有一个函数叫做 GetName() B C 中都将有这一函数成员,这个时候 D :: GetName() 的真正实现是来自 B 的还是 C 的呢?二义性出现了 (ambiguity)

不过如果真的发生了这种情况,要解决的方法也不是没有,可以这样做:

D d;
d.B::GetName(); 
// Calling B's implementation

嗯,很容易理解。

另外一个高阶一点的方法叫做虚继承 (virtual inheritance) 。对于在虚拟继承中的父类,其中的成员都保证不会在后面的子类中出现二义现象 (ambiguity) 。似乎是专门为了 MI 才整出来的,汗 ……

例子还是已前面的钻石问题:

class  A
{
   
public :
      
void  GetName();
// ..
};

class  B :  virtual   public  A
{
// ..
};

class  C :  virtual   public  A
{
// ..
};

class  D :  public  B,  public  C
{
// ..
}

D d;
d.GetName(); 
// there is no ambiguity here.

但是虚继承不是没有代价的,大师说这种技术会使得最终代码变得更大,访问虚拟继承中的父类成员也会变得更慢一些。

这个也不难理解。和空间换时间一样,和不给牛吃草牛就不干活一样。 ( 另外的一个代价我还没能完全理解透彻:书上说因为虚继承中基类的初始化是由继承关系中最底层的子类负责的,因此对于这些最底下的 嫡孙 类来说,就不是那么方便了 )

于是大师建议只有在必要的时候才使用虚继承,而在虚继承中的基类里也不要放置数据成员,这样就不用担心初始化的问题了。

不过存在就是合理,还是有需要用到 MI 的时候。一个在书中提到的使用 MI 的情形是:当需要从一个类 AClass 中继承接口,又需要从另外一个类 BClass 中继承实现细节时,就可以考虑在公有继承 AClass 的同时又私有继承 BClass 。道理大致就是这样,就不编造程序画蛇添足了。

总结一下: MI SI(Single Inheritance) 要复杂容易出错 ( 比如说钻石问题 ) ,即使可以用虚继承来解决钻石问题,但是其带来的代码体积增大,访问效率下降以及初始化问题还是不能忽视的。最后话说回来,需要用到 MI 的时候,小心点用便是 @# %

【参考】 http://en.wikipedia.org/wiki/Virtual_inheritance

你可能感兴趣的:(读书笔记:Effective C++ 炒冷饭 - Item 40 小心使用多重继承)