多重继承:使用跳过虚拟'关键字并拒绝钻石层次结构?

时间:2017-08-02 22:59:33

标签: c++ multiple-inheritance virtual-inheritance

我了解如何以及为什么必须使用virtual关键字来解决"钻石问题",并创建一个类层次结构:

   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关键字)我们要创建一个"钻石&#34 ;班级等级?为什么编译器不会自动生成它? 如果我们不使用virtual,编译器将在下面生成类层次结构:

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

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

2 个答案:

答案 0 :(得分:1)

考虑Avisitor接口的情况,而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子对象是可取且必要的。在此示例中删除虚拟继承允许它进行编译。

答案 1 :(得分:0)

  

是否存在第二层次结构有用且有效的编程情况?

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

  

为什么我们必须告诉编译器(使用virtual关键字)我们想要创建一个“菱形”类层次结构?为什么编译器不会自动生成它?

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

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

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