为什么必须由派生类最多的类构造虚拟基类?

时间:2017-06-02 08:47:31

标签: c++ inheritance virtual-inheritance

以下代码不会编译:

class A {
public:
    A(int) {}
};

class B: virtual public A {
public:
    B(): A(0) {}
};

// most derived class
class C: public B {
public:
    C() {} // wrong!!!
};

如果我在A的构造函数初始化列表中调用C的构造函数,那就是:

// most derived class
class C: public B {
public:
    C(): A(0) {} // OK!!!
};

它确实有用。

显然,原因是虚拟基类必须始终由大多数派生类构建。

我不明白这种限制背后的原因。

3 个答案:

答案 0 :(得分:26)

因为它避免了这个:

class A {
public:
    A(int) {}
};

class B0: virtual public A {
public:
    B0(): A(0) {}
};

class B1: virtual public A {
public:
    B1(): A(1) {}
};

class C: public B0, public B1 {
public:
    C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};

答案 1 :(得分:8)

因为在具有虚拟继承的基类的类层次结构中,基类将/可能由多个类共享(例如,在菱形继承中,其中相同的基类由多个类继承)。这意味着,只有一个虚拟继承的基类副本。它本质上意味着,必须首先构造基类。它最终意味着派生类必须实例化给定的基类。

例如:

class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 // A is shared, and would have one copy only.

答案 2 :(得分:4)

我发现此规则容易出错并且很麻烦(但是,多重继承的哪一部分不是?)。

但逻辑强加的构造顺序必须与正常(非虚拟)继承的情况不同。考虑Ajay的例子,减去虚拟:

class A;
class B1 : A;
class B2 : A;
class C: B1,B2

在这种情况下,对于每个C两个As构造,一个作为B1构造的一部分,另一个作为B2构造的一部分。 B类的代码负责,并且可以做到。事件的顺序是:

Start C ctor
   Start B1 ctor
      A ctor (in B's ctor code)
   End B1 ctor
   Start B2 ctor
      A ctor (in B's ctor code)
   End B2 ctor
End C ctor

现在考虑

中的虚拟继承
class A;
class B1 : virtual A;
class B2 : virtual A;
class C: B1,B2 

一个事件顺序是

Start C ctor
   Start B1 ctor
      // NO A ctor
   End B1 ctor
   Start B2 ctor
      // NO A ctor
   End B2 ctor

   A ctor // not B's code!
End C ctor

A可能在B1和B2之前构建,但这是无关紧要的。重要的是,在其他基地建设期间,A的建设不会发生。该代码根本不执行,并且无法执行,因为类型A的虚拟继承的基类子对象是的最大派生类的控制,并且无法从B1和B2的代码;事实上,在构建B1或B2时,C甚至还没有完全构建,并且可能会尝试在C中创建A.