65

http://en.wikipedia.org/wiki/Diamond_problem

我知道这意味着什么,但我可以采取哪些措施来避免它?

4

8 回答 8

75

一个实际的例子:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

注意 D 类是如何从 B 和 C 继承的。但是 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 {};
于 2008-09-26T12:57:52.653 回答
14

虚拟继承。这就是它的用途。

于 2008-09-26T01:39:24.183 回答
13

我会坚持只使用接口的多重继承。虽然类的多重继承有时很有吸引力,但如果您经常依赖它,它也会令人困惑和痛苦。

于 2008-09-26T01:49:50.880 回答
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;
};

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

于 2008-09-27T06:09:02.643 回答
3
class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

在这种情况下,A 类的属性在 D 类中重复了两次,这会增加内存使用量……因此,为了节省内存,我们为 A 类的所有继承属性创建了一个虚拟属性,这些属性存储在 Vtable 中。

于 2012-07-15T19:07:43.273 回答
2

好吧,可怕的钻石的伟大之处在于它发生时是一个错误。最好的避免方法是事先弄清楚你的继承结构。例如,我从事的一个项目有查看器和编辑器。Editors 是 Viewer 的逻辑子类,但由于所有 Viewer 都是子类 - TextViewer、ImageViewer 等,Editor 不是从 Viewer 派生的,因此允许最终的 TextEditor、ImageEditor 类避免菱形。

在无法避免菱形的情况下,使用虚拟继承。然而,对于虚拟基,最大的警告是虚拟基的构造函数必须由派生最多的类调用,这意味着虚拟派生的类无法控制构造函数参数。此外,虚拟基地的存在往往会在通过链条施放时导致性能/空间损失,尽管我不认为除了第一个之外会有太多的损失。

另外,如果您明确说明要使用哪个底座,则始终可以使用菱形。有时这是唯一的方法。

于 2008-09-26T02:01:42.787 回答
1

我会建议一个更好的类设计。我确信通过多重继承可以最好地解决一些问题,但首先检查是否有其他方法。

如果没有,请使用虚拟功能/接口。

于 2008-09-26T12:50:37.973 回答
0

通过委托使用继承。然后两个类都将指向一个基 A,但必须实现重定向到 A 的方法。它具有将 A 的受保护成员转换为 B、C 和 D 中的“私有”成员的副作用,但现在你不需要需要虚拟,而你没有钻石。

于 2011-02-11T23:24:59.943 回答