13

Assume classes D and E and F all inherit from base class B, and that class C inherits from D and E.

(i) How many copies of class B appear in class C?

(ii) How would using virtual inheritance change this scenario? 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++?

Here are some of my current ideas, but I'm an by no means an expert on 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? Then if C inherits from D and E that would mean there are 2 copies of B in C.

(ii) Using virtual is somewhat similar to using Abstract in Java (i think). 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. 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". 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".

(iii) My idea here is that Java can use interfaces to avoid the use of multiple inheritance. 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.

4

2 回答 2

3

(i) 和 (iii) 是对的。无论如何,根据我的经验,在 C++ 中,当我使用多重继承时,大部分时间都是因为基础是接口(C++ 中不支持关键字的概念,但它是一个无论如何都可以执行的概念)。

(ii)的第一句话是对的,但是您的第二句话是在谈论虚拟函数,这与虚拟继承完全不同。虚拟继承意味着只有一个 的副本B,并且DE都具有相同的副本作为它们的基础。在函数方面没有区别,但区别在于B.

如果有打印出B成员变量的函数foo;然后在情况 (ii) 中,此函数总是打印相同的值,因为只有一个foo,但在情况 (i) 中,从基类调用该函数D可能会打印与从基类调用不同的值E

“钻石继承”这个词用两个词来概括这一切,作为一个很好的助记符:)

于 2014-05-10T22:25:39.897 回答
1

尽管推理需要工作,但您似乎大部分都得出了正确的答案。这里的关键问题是“如果 C 实例继承了两次相同的基类,如何布置内存?”

i) 对于 C 类型的对象,在内存布局中有 2 个基类 B 的副本。提供的示例是“菱形继承”的情况,因为当您绘制依赖/继承树时,实际上是绘制了一个菱形. 钻石继承的“问题”本质上是询问如何在内存中放置对象。C++ 采用了两种方法,一种是快速的 this,复制数据成员,另一种是较慢的,“虚拟继承”。采用非虚拟方法的原因是,如果您继承一个没有数据成员的类(Java 中的接口),那么“复制数据成员”就没有问题,因为它们不存在(见我在底部的注释)。如果您的计划是仅使用单继承,则建议使用非虚拟继承。

ii) 如果你有一个virtual class C,那么这就是在 C++ 语言中你想让编译器执行英雄行为以确保在你的派生的内存布局中只存在任何/所有基类的一个副本的方式班级; 我相信这也会对性能造成轻微影响。如果您现在使用“C”实例中的任何“B”成员,它将始终引用内存中的同一位置。请注意,虚拟继承与您的函数是否为虚拟无关。

旁白:这也与抽象类的概念完全无关。要在 C++ 中创建类抽象,请将任何方法声明设置为 0,如void foo() = 0;; 对任何方法(包括析构函数)这样做就足以使整个类抽象。

iii) Java 完全禁止它。在 Java 中,只有单一继承加上实现任意数量接口的能力。虽然接口确实授予您“is-a”关系和拥有虚函数的能力,但它们隐含地避免了 C++ 在数据布局和菱形继承方面的问题,因为接口不能添加任何数据成员,事实上:没有关于如何解析任何数据成员的位置的困惑。

对 iii 的一个重要扩展是,如果您碰巧“两次实现相同的接口”,那么虚拟函数调用调度根本不会受到影响。原因是该方法将始终执行相同的操作,即使您的虚拟表中有多个副本;它只作用于你的类的数据,它本身不包含需要消除歧义的数据。

于 2014-05-11T04:14:57.673 回答