使用多重继承时如何避免死亡钻石?

时间:2020-03-06 14:45:39  来源:igfitidea点击:

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;
};

是的,访问权限是不同的,但是如果我们可以采用这种方法,而又无需重复代码,那会更好,因为它的功能不那么强大。 (当我们别无选择时,可以节省电源。)