C++ 使用多重继承时如何避免死亡之钻?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/137282/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I avoid the Diamond of Death when using multiple inheritance?
提问by ilitirit
http://en.wikipedia.org/wiki/Diamond_problem
http://en.wikipedia.org/wiki/Diamond_problem
I know what it means, but what steps can I take to avoid it?
我知道这意味着什么,但我可以采取哪些措施来避免它?
回答by Mark Ingram
A practical example:
一个实际例子:
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
Notice how class D inherits from both B & C. But both B & C inherit from A. That will result in 2 copies of the class A being included in the vtable.
注意类 D 如何从 B 和 C 继承。但是 B 和 C 都从 A 继承。这将导致类 A 的 2 个副本包含在 vtable 中。
To solve this, we need virtual inheritance. It's class A that needs to be virtually inherited. So, this will fix the issue:
为了解决这个问题,我们需要虚拟继承。它是需要虚拟继承的 A 类。所以,这将解决这个问题:
class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
回答by eduffy
virtual inheritance. That's what it's there for.
虚拟继承。这就是它的用途。
回答by Bob Somers
I'd stick to using multiple inheritance of interfaces only. While multiple inheritance of classes is attractive sometimes, it can also be confusing and painful if you rely on it regularly.
我会坚持只使用接口的多重继承。虽然类的多重继承有时很有吸引力,但如果您经常依赖它,它也会令人困惑和痛苦。
回答by Bob Somers
Inheritance is a strong, strong weapon. Use it only when you really need it. In the past, diamond inheritance was a sign that I was going to far with classification, saying that a user is an "employee" but they are also a "widget listener", but also a ...
传承是一种强而有力的武器。仅在您真正需要时才使用它。过去,钻石继承是一个标志,我要远离分类,说用户是“员工”但他们也是“小部件侦听器”,但也是......
In these cases, it's easy to hit multiple inheritance issues.
在这些情况下,很容易遇到多重继承问题。
I solved them by using composition and pointers back to the owner:
我通过使用组合和指向所有者的指针解决了它们:
Before:
前:
class Employee : public WidgetListener, public LectureAttendee
{
public:
Employee(int x, int y)
WidgetListener(x), LectureAttendee(y)
{}
};
After:
后:
class Employee
{
public:
Employee(int x, int y)
: listener(this, x), attendee(this, y)
{}
WidgetListener listener;
LectureAttendee attendee;
};
Yes, access rights are different, but if you can get away with such an approach, without duplicating code, it's better because it's less powerful. (You can save the power for when you have no alternative.)
是的,访问权限是不同的,但是如果您可以使用这种方法而无需重复代码,那就更好了,因为它的功能不那么强大。(当您别无选择时,您可以节省电量。)
回答by NItish
class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};
In this the attributes of Class A repeated twice in Class D which makes more memory usage... So to save memory we make a virtual attribute for all inherited attributes of class A which are stored in a Vtable.
在这种情况下,A 类的属性在 D 类中重复了两次,这会占用更多内存......因此,为了节省内存,我们为存储在 Vtable 中的 A 类所有继承属性创建了一个虚拟属性。
回答by coppro
Well, the great thing about the Dreaded Diamond is that it's an error when it occurs. The best way to avoid is to figure out your inheritance structure beforehand. For instance, one project I work on has Viewers and Editors. Editors are logical subclasses of Viewers, but since all Viewers are subclasses - TextViewer, ImageViewer, etc., Editor does not derive from Viewer, thus allowing the final TextEditor, ImageEditor classes to avoid the diamond.
好吧,可怕的钻石的伟大之处在于它发生时是一个错误。避免的最好方法是事先弄清楚您的继承结构。例如,我从事的一个项目有查看器和编辑器。Editors是Viewers的逻辑子类,但是由于所有Viewer都是子类——TextViewer、ImageViewer等,Editor并不是从Viewer派生出来的,从而让最终的TextEditor、ImageEditor类避开了菱形。
In cases where the diamond is not avoidable, using virtual inheritance. The biggest caveat, however, with virtual bases, is that the constructor for the virtual base mustbe called by the most derived class, meaning that a class that derives virtually has no control over the constructor parameters. Also, the presence of a virtual base tends to incur a performance/space penalty on casting through the chain, though I don't believe there is much of a penalty for more beyond the first.
在钻石无法避免的情况下,使用虚拟继承。然而,对于虚拟基类,最大的警告是虚拟基类的构造函数必须由派生最多的类调用,这意味着虚拟派生的类无法控制构造函数参数。此外,虚拟基地的存在往往会在通过链进行投射时导致性能/空间损失,尽管我认为除了第一个之外不会有太大的损失。
Plus, you can always use the diamond if you are explicit about which base you want to use. Sometimes it's the only way.
另外,如果您明确要使用哪个基地,您始终可以使用菱形。有时这是唯一的方法。
回答by user17720
I would suggest a better class design. I'm sure there are some problems that are solved best through multiple inheritance, but check to see if there is another way first.
我会建议一个更好的班级设计。我确信有些问题可以通过多重继承得到最好的解决,但请先检查是否有其他方法。
If not, use virtual functions/interfaces.
如果没有,请使用虚拟功能/接口。
回答by Lee Louviere
Use inheritance by delegation. Then both classes will point to a base A, but have to implement methods that redirect to A. It has the side effect of turning protected members of A into "private" members in B,C, and D, but now you don't need virtual, and you don't have a diamond.
使用委托继承。然后两个类都将指向基 A,但必须实现重定向到 A 的方法。它具有将 A 的受保护成员变成 B、C 和 D 中的“私有”成员的副作用,但现在您没有需要虚拟,而你没有钻石。