使用多重继承时如何避免死亡钻石?
http://en.wikipedia.org/wiki/Diamond_problem
我知道这意味着什么,但是我可以采取哪些步骤来避免这种情况?
解决方案
虚拟继承。那就是它的目的。
我会坚持只使用接口的多重继承。尽管类的多重继承有时很有吸引力,但是如果我们经常依赖它,也可能会造成混乱和痛苦。
好吧,关于可怕的钻石的伟大之处在于,它发生时是一个错误。避免的最佳方法是事先弄清楚继承结构。例如,我从事的一个项目有查看器和编辑器。编辑器是Viewer的逻辑子类,但是由于所有Viewer都是TextViewer,ImageViewer等的子类,因此Editor不派生自Viewer,因此最终的TextEditor和ImageEditor类可以避免出现菱形。
在无法避免的情况下,请使用虚拟继承。但是,对于虚拟基础,最大的警告是,虚拟基础的构造函数必须由最派生的类调用,这意味着派生的类实际上无法控制构造函数的参数。同样,虚拟基础的存在往往会在通过链进行投射时导致性能/空间损失,尽管我不认为在第一个基础之上会有更多的损失。
另外,如果我们明确要使用哪个基准,则始终可以使用菱形。有时这是唯一的方法。
我建议一个更好的课堂设计。我敢肯定,通过多重继承可以最好地解决某些问题,但是请先检查是否有另一种方法。
如果没有,请使用虚拟功能/接口。
一个实际的例子:
class A {}; class B : public A {}; class C : public A {}; class D : public B, public C {};
请注意,类D是如何从B和C继承的。但是B和C都是从A继承的。这将导致vtable中包含类A的2个副本。
为了解决这个问题,我们需要虚拟继承。实际上需要继承的是A类。因此,这将解决问题:
class A {}; class B : virtual public A {}; class C : virtual public A {}; class D : public B, public C {};
继承是强大的武器。仅在确实需要时使用它。过去,钻石继承标志着我对分类的意义不大,说用户是"雇员",但他们也是"小工具侦听器",也是...
在这些情况下,很容易遇到多个继承问题。
我通过使用组合和指向所有者的指针解决了它们:
前:
class Employee : public WidgetListener, public LectureAttendee { public: Employee(int x, int y) WidgetListener(x), LectureAttendee(y) {} };
后:
class Employee { public: Employee(int x, int y) : listener(this, x), attendee(this, y) {} WidgetListener listener; LectureAttendee attendee; };
是的,访问权限是不同的,但是如果我们可以采用这种方法,而又无需重复代码,那会更好,因为它的功能不那么强大。 (当我们别无选择时,可以节省电源。)