http://en.wikipedia.org/wiki/Diamond_problem
我知道这意味着什么,但我可以采取哪些措施来避免它?
答案 0 :(得分:64)
一个实际的例子:
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
注意D类如何从B& B继承。但是B& B都是C继承自A.这将导致类别A的2个副本包含在vtable中。
要解决这个问题,我们需要虚拟继承。它的A类需要实际上是遗传的。所以,这将解决问题:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
答案 1 :(得分:14)
虚拟继承。这就是它的用途。
答案 2 :(得分:12)
我坚持只使用接口的多重继承。虽然类的多重继承有时很有吸引力,但如果你经常依赖它,它也会让人感到困惑和痛苦。
答案 3 :(得分:6)
继承是一种强大而有力的武器。只有在你真正需要时才使用它。在过去,钻石继承是我在分类方面走得很远的一个标志,说用户是“员工”,但他们也是“小工具听众”,但也是... ...
在这些情况下,很容易遇到多个继承问题。
我通过将构图和指针反馈给所有者来解决它们:
在:
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;
};
是的,访问权限是不同的,但如果您可以使用这种方法,而不重复代码,那就更好了,因为它不那么强大。 (当你别无选择时,你可以节省电力。)
答案 4 :(得分:3)
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
在这里,A类的属性在D类中重复两次,这使得内存使用更多......所以为了节省内存,我们为存储在Vtable中的A类的所有继承属性创建了一个虚拟属性。
答案 5 :(得分:1)
嗯,关于Dreaded Diamond的好处在于它发生时是一个错误。要避免的最好方法是事先弄清楚你的继承结构。例如,我工作的一个项目有观众和编辑。编辑器是Viewers的逻辑子类,但由于所有的Viewers都是子类 - TextViewer,ImageViewer等,因此编辑器不会派生自Viewer,因此允许最终的TextEditor,ImageEditor类避免钻石。
如果钻石无法避免,请使用虚拟继承。然而,对虚拟基础最大的警告是,虚拟基础的构造函数必须由最派生类调用,这意味着实际派生的类无法控制构造函数参数。此外,虚拟基地的存在往往会导致通过链条的性能/空间损失,但我不认为除了第一个之外还有更多的惩罚。
另外,如果您明确要使用哪个基础,则可以随时使用钻石。有时它是唯一的方式。
答案 6 :(得分:0)
我建议更好的课堂设计。我确信有一些问题可以通过多重继承得到最好的解决,但请先查看是否还有另一种方法。
如果没有,请使用虚拟功能/接口。
答案 7 :(得分:0)
通过委派使用继承。然后这两个类都将指向一个基类A,但必须实现重定向到A的方法。它具有将A的受保护成员转换为B,C和D中的“私有”成员的副作用,但现在你没有需要虚拟,你没有钻石。