C++】多重继承,救世主还是鸡肋

来源:互联网 发布:淘宝退款不退货违法 编辑:程序博客网 时间:2024/03/29 05:47

总有许多人在讨论C++的是是非非,我是人云亦云那个。

当你试图拥有两个以上类的特性时:

1 组合,用一个新类把那两个类的对象包含起来。很方便,很简单。缺点是:首先,会有太多的接口函数存在于两个基类对象与外部之间。你想要用他们,又不得不将他们封装起来。好吧,写接口函数吧。其次:有一天,当子类实现改变的时候,特别是要摒弃两个基类的时候,你不得不做相当多的代码修改(关于接口函数)。有人说:我不用接口函数,直接将两个基类暴露在外面,不就省去了接口函数吗?……好吧,不过这违背了类封装的规则。

2 两个或者更多的单继承。这样做无异养虎为患!你非要把两个可能毫不相干的基类嫁接到一起,意味着必将产生一些无实际用处的过度类。这会使你的继承树变得更深,更加难以维护。这绝对不是一件好事情。

3 最后的选择就是多重继承,一把双刃剑。

多重继承的优点很明显:简单,清晰,更有利于复用。不会因为基类一个小小的改变而大张旗鼓去改代码。

缺点也很明显:首先,二义性。两个基类中有同名方法的时候,你不得不在子类的调用中指明此方法出自那个基类。这看起来有些麻烦,幸好在你迷糊的时候,编译器会提醒你。其次:假如类A派生了B和C,而B和C共同派生了D,麻烦就出现了。这种中间大两头小的继承树有个形象的名字:钻石型继承树(DOD:Diamond Of Death)。从名字看此君绝非善类,事实也如此,A是D的父类没错,但是有两条路径。这样的数据组织方式会有一些难以预料的后果。除去二义性不说,想想吧,D中有多少个看似重复的方法,有多少个名字相同的数据成员!

不惜一切代价,避免DOD的出现。除非,你认为DOD出现在这里是最恰当不过的,而且,确保你你使用了虚基类(虚继承),确保你对每个类的细节都完全清楚,确保你知道虚基类(虚继承)的副作用。

多重继承还会带来一些其他的问题:使用父类指针指向子类对象变成了一件复杂的事情。你不得不用到C++中提供的dynamic_cast来执行强制转换。至于dynamic_cast,也是个麻烦的家伙,它是在运行期间而非编译期间进行转换的(因为编译期间它不能确定到底要转向一个什么类型),因此除了会带来一些轻微的性能损失,它要求编译器允许RTTI(Runtime Type Information,运行时类型信息),也就是要求编译器保存所有类在运行时的信息。

多重继承还会使得子类的vtable变得不同寻常。单继承的vtable只是在父类vtable的表尾加上新的虚函数,子类对象的vtable中包含了有序的父类vtable。而对于多重继承,两个父类可能有完全不同的vtable,因此,子类的vtable中绝对不可能包含完整的有序的两个父类的vtable。子类的vtable中可能包含了两块不相连的父类vtable,因此每个父类都被迫追加了一个vtable,也就是,每个父类的对象都添加了一个指针。

孰优孰劣,自己把握。没有永远最好的,只有当前适合的。Java中摒弃了多重继承可能也是出于太过复杂,可能有不可料知的结果的原因。

不要随意使用多重继承。大多数的情况,用容器(也就是类的组合法)会更好些。

原创粉丝点击