简体   繁体   English

钻石继承

[英]Diamond inheritance

Assume classes D and E and F all inherit from base class B, and that class C inherits from D and E. 假设类D和E和F都从基类B继承,并且类C继承自D和E.

(i) How many copies of class B appear in class C? (i)B类有多少副本出现在C类?

(ii) How would using virtual inheritance change this scenario? (ii)如何使用虚拟继承改变这种情况? Explain your answer. 解释你的答案。

(iii) How does Java avoid the need for multiple inheritance for many of the situations where multiple inheritance might be used in C++? (iii)对于许多可能在C ++中使用多重继承的情况,Java如何避免需要多重继承?

Here are some of my current ideas, but I'm an by no means an expert on C++! 以下是我目前的一些想法,但我绝不是C ++方面的专家!

(i) If C inherits from D and E which are subclasses of B, then would D and E technically be copies of their super class? (i)如果C继承自作为B的子类的D和E,那么D和E在技术上是否是他们的超类的副本? Then if C inherits from D and E that would mean there are 2 copies of B in C. 然后,如果C继承自D和E,那意味着C中有2个B副本。

(ii) Using virtual is somewhat similar to using Abstract in Java (i think). (ii)使用虚拟有点类似于在Java中使用Abstract(我认为)。 Now given this, it would mean that there would not be multiple copies of B in C, as the instantiation would be cascaded down to the level it is needed. 现在给出这个,这意味着在C中不会有多个B副本,因为实例化将被级联到所需的级别。 I am not sure how to word my explanation but say B has a function called print() which prints "i am B" and C overrides this function put prints "i am C". 我不确定如何说出我的解释但是说B有一个叫做print()的函数打印“我是B”而C覆盖了这个函数put print“我是C”。 If you called print() on C without virtual you end up printing "i am B", using virtual would mean that it would print "i am C". 如果您在没有虚拟的情况下在C上调用print(),则最终打印“我是B”,使用虚拟将意味着它将打印“我是C”。

(iii) My idea here is that Java can use interfaces to avoid the use of multiple inheritance. (iii)我的想法是Java可以使用接口来避免使用多重继承。 You can implement multiple interfaces but you can only extend one Class. 您可以实现多个接口,但只能扩展一个类。 I'm not sure what else to add here, so any input or relevant resources would be helpful. 我不确定还有什么要添加,所以任何输入或相关资源都会有所帮助。

(i) and (iii) are right. (i)和(iii)是对的。 In my experience anyway, most of the time in C++ when I've used multiple inheritance it's been because the bases were interfaces (a concept which doesn't have keyword support in C++, but it is a concept you can execute anyway). 根据我的经验,大部分时间在C ++中,当我使用多重继承时,这是因为基础是接口(这个概念在C ++中没有关键字支持,但它是一个你可以执行的概念)。

The first sentence of (ii) is right, however your second sentence is talking about virtual functions, which is completely different to virtual inheritance. (ii)的第一句是正确的,但是你的第二句话是关于虚函数,它与虚函数继承完全不同。 Virtual inheritance means that there is only one copy of B , and the D and E both have that same copy as their base. 虚拟继承意味着只有一个B副本,而DE都具有与其基础相同的副本。 There is no difference in terms of functions, but the difference comes in terms of member variables (and base classes) of B . 在函数方面没有区别,但区别在于B的成员变量(和基类)。

If there is a function that prints out B 's member variable foo ; 如果有一个函数打印出B的成员变量foo ; then in case (ii) this function always prints the same value because there is only one foo , but in case (i) calling that function from the D base class may print a different value to calling it from the E base class. 然后在情况(ii)中,这个函数总是打印相同的值,因为只有一个foo ,但是在情况下(i)从D基类调用该函数可能会打印一个不同的值来从E基类调用它。

The term "diamond inheritance" wraps all this up in two words that serve as a good mnemonic :) “钻石继承”一词用两个词作为一个好的助记符来包装所有这些:)

You seem to have mostly arrived at the right answers, though the reasoning needs work. 你似乎已经得到了正确的答案,尽管推理需要有效。 The key issue at play here is the question of "how to lay out the memory of an instance of C if it inherits the same base class twice?" 这里的关键问题是“如果它继承两个相同的基类,如何布置C实例的内存?”

i) There are 2 copies of the base class B in the memory layout for an object of type C. The example provided is a case of "diamond inheritance", because when you draw out the dependency/inheritance tree, you essentially draw a diamond. i)在类型C的对象的内存布局中有2个基类B的副本。提供的示例是“钻石继承”的情况,因为当您绘制依赖/继承树时,您基本上绘制钻石。 The "problem" with diamond inheritance is essentially to ask how to lay the object out in memory. 钻石继承的“问题”主要是询问如何将对象放在内存中。 C++ went with two approaches, a fast one, this, duplicating the data members, and a slower one, "virtual inheritance". C ++采用两种方法,一种是快速的,一种是复制数据成员,一种是较慢的一种,即“虚拟继承”。 The reason to take the non-virtual approach is that if you inherit a class that has no data members (what would be an interface in Java), then there is no problem with "duplicating the data members", because they do not exist (see my note at the bottom). 采用非虚方法的原因是,如果继承一个没有数据成员的类(Java中的接口是什么),那么“复制数据成员”就没有问题,因为它们不存在(看到我在底部的笔记)。 It is also advisable to use non-virtual inheritance if your plan is to only use single inheritance. 如果您的计划仅使用单继承,则建议使用非虚拟继承。

ii) If you have a virtual class C , then that is the way of saying in the C++ language that you would like to have the compiler perform acts of heroism to ensure that only one copy of any/all base classes exist in the memory layout of your derived class; ii)如果你有一个virtual class C ,那么在C ++语言中你想让编译器执行英雄行为以确保内存布局中只存在任何/所有基类的一个副本派生类的; I believe this also incurs a slight performance hit. 我相信这也会带来轻微的性能损失。 If you use any 'B' members from a 'C' instance now, it will always refer to the same place in memory. 如果你现在使用'C'实例中的任何'B'成员,它将始终引用内存中的相同位置。 Note that virtual inheritance has no bearing on whether your functions are virtual. 请注意,虚拟继承与您的函数是否为虚函数无关。

Aside: This also is completely unrelated to the concept of a class being abstract. 旁白:这也与抽象类的概念完全无关。 To make a class abstract in C++, set any method declaration = 0, as in void foo() = 0; 要在C ++中创建类抽象,请设置任何方法声明= 0,如void foo() = 0; ; ; doing so for any method (including the destructor) is sufficient to make the entire class abstract. 对任何方法(包括析构函数)这样做都足以使整个类抽象化。

iii) Java outright forbids it. iii)Java彻底禁止它。 In Java there is only single inheritance plus the ability to implement any number of interfaces. 在Java中,只有单个继承以及实现任意数量接口的能力。 While interfaces do grant you the "is-a" relationship and the ability to have virtual functions, they implicitly avoid the issues that C++ has with data layouts and diamond inheritance, as an interface cannot add any data members, ipso facto: there is no confusion about how to resolve any data member's location. 虽然接口确实授予您“is-a”关系和具有虚拟功能的能力,但它们隐含地避免了C ++对数据布局和钻石继承的问题,因为接口无法添加任何数据成员,事实上:没有关于如何解决任何数据成员的位置的困惑。

An important extension to iii is to realize that virtual function call dispatch is not impacted at all if you happen to "implement the same interface twice". iii的一个重要扩展是认识到,如果你碰巧“两次实现相同的接口”,虚拟函数调用不会受到影响。 The reason is that the method will always do the same thing, even if there were multiple copies of it in your virtual table; 原因是该方法将始终执行相同的操作,即使它在您的虚拟表中有多个副本; it only acts on the data of your class, it does not itself contain data that needs to be disambiguated. 它只对您的类的数据起作用,它本身不包含需要消除歧义的数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM