多重继承:使用跳过 'virtual' 关键字并拒绝菱形层次结构?

Multiple inheritance: usage of skipping 'virtual' keyword and refusing the diamond hierarchy?

我明白我们如何以及为什么必须使用 virtual 关键字来解决 "diamond problem",并创建一个 class 层次结构,如下所示:

   A
  / \
 /   \
B     C
 \   /
  \ /
   D

代码示例:

class A { int a; public: A(int a) : a(a) {} };
class B : public virtual A { int b; public: B(int a, int b) : A(a), b(b) {} };
class C : public virtual A { int c; public: C(int a, int c) : A(a), c(c) {} };
class D : public B, public C { public: D(int a, int b, int c) : A(a), B(0, b), C(0, c) {} };

我找不到问题的答案:为什么我们必须告诉编译器(使用 virtual 关键字)我们要创建一个 "diamond" class等级制度?为什么编译器不自动生成? 如果我们不使用 virtual,编译器会生成下面的 class 层次结构:

A     A
|     |
|     |
B     C
 \   /
  \ /
   D

是否存在第二层次有用且有效的编程情况? 编辑(让我的问题说清楚):他们为什么要我们使用virtual?我想,原因是他们想给我们一个选择。第二个 class 层次结构是最佳选择的任何示例?

Is there any programming situation where the second hierarchy is useful and works?

虽然可能有,但在我回答了另一个问题后,这个问题(在我看来)变得不那么有趣了。

Why do we have to tell the compiler (using the virtual keyword) that we want to create a "diamond" class hierarchy? Why does the compiler not generate it automatically?

不行,编译器不能自动生成。这就是为什么当你希望继承是虚拟的时,你必须明确地告诉编译器。

假设存在这样的自动化,为了让编译器猜测 B 的基数 A 应该是虚拟的,它必须知道 A 也是基数CBC 都是 D 的基。因此,B 的定义的含义将取决于 D 的定义(这也取决于 C。本质上,属于同一层次结构的所有 类 将依赖于该层次结构中的所有其他 类,最顶层除外)。但是层次结构的某些 类 可能在完全不同的翻译单元中定义,这些翻译单元可能在与其他翻译单元完全不同的时间编译。 C++ 编译器根本不能假设它知道所有 类.

有一种方法可以实现这一点:使所有继承虚拟化。然而,虚拟继承有一些(小的,但非零的)开销,所以这不是一个理想的解决方案。此外,虚拟继承使得 static_cast 无法派生类型,因此此类转换需要 dynamic_cast,这需要 运行 时间类型信息,并且有兴趣允许缺乏 RTTI 支持的有限 C++ 实现。

考虑 A 是一个 visitor 接口的情况,而 BC 彼此独立地实现这个接口,并且需要不同的访问行为。

class A
{
public:
    virtual void visit(int) = 0;
};

class B : virtual private A
{
public:
    virtual void visit(int) { }
};

class C : virtual private A
{
public:
    virtual void visit(int) { }
};

class D : public B, public C { };
// error: virtual function 'A::visit' has more than one final overrider in 'D'

这个does not compile在这种情况下 BC 保留单独的 A 子对象是可取和必要的。在这个例子中删除虚拟继承允许它编译。