http://en.wikipedia.org/wiki/Diamond_problem
我知道这意味着什么,但我可以采取哪些措施来避免它?
一个实际的例子:
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 {};
虚拟继承。这就是它的用途。
我会坚持只使用接口的多重继承。虽然类的多重继承有时很有吸引力,但如果您经常依赖它,它也会令人困惑和痛苦。
传承是一种强大的、强大的武器。仅在您真正需要时才使用它。过去,钻石继承是我在分类方面走得很远的标志,说用户是“员工”,但他们也是“小部件侦听器”,但也是...
在这些情况下,很容易遇到多重继承问题。
我通过使用组合和指向所有者的指针来解决它们:
前:
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;
};
是的,访问权限是不同的,但是如果您可以采用这种方法而无需复制代码,那就更好了,因为它的功能不那么强大。(当你别无选择时,你可以节省电力。)
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
在这种情况下,A 类的属性在 D 类中重复了两次,这会增加内存使用量……因此,为了节省内存,我们为 A 类的所有继承属性创建了一个虚拟属性,这些属性存储在 Vtable 中。
好吧,可怕的钻石的伟大之处在于它发生时是一个错误。最好的避免方法是事先弄清楚你的继承结构。例如,我从事的一个项目有查看器和编辑器。Editors 是 Viewer 的逻辑子类,但由于所有 Viewer 都是子类 - TextViewer、ImageViewer 等,Editor 不是从 Viewer 派生的,因此允许最终的 TextEditor、ImageEditor 类避免菱形。
在无法避免菱形的情况下,使用虚拟继承。然而,对于虚拟基,最大的警告是虚拟基的构造函数必须由派生最多的类调用,这意味着虚拟派生的类无法控制构造函数参数。此外,虚拟基地的存在往往会在通过链条施放时导致性能/空间损失,尽管我不认为除了第一个之外会有太多的损失。
另外,如果您明确说明要使用哪个底座,则始终可以使用菱形。有时这是唯一的方法。
我会建议一个更好的类设计。我确信通过多重继承可以最好地解决一些问题,但首先检查是否有其他方法。
如果没有,请使用虚拟功能/接口。
通过委托使用继承。然后两个类都将指向一个基 A,但必须实现重定向到 A 的方法。它具有将 A 的受保护成员转换为 B、C 和 D 中的“私有”成员的副作用,但现在你不需要需要虚拟,而你没有钻石。