虚拟继承

使用继承时,你可以指定 virtual 关键字:

struct A{};
struct B: public virtual A{};

当类 B 具有虚拟基础 A 时,意味着 A 将驻留在大多数派生类的继承树中,因此大多数派生类也负责初始化该虚拟基础:

struct A
{
    int member;
    A(int param)
    {
        member = param;
    }
};

struct B: virtual A
{
    B(): A(5){}
};

struct C: B
{
    C(): /*A(88)*/ {}
};

void f()
{
    C object; //error since C is not initializing it's indirect virtual base `A`
}

如果我们取消评论/*A(88)*/,我们将不会收到任何错误,因为 C 现在正在初始化它的间接虚拟基础 A

还要注意,当我们创建变量 object 时,大多数派生类都是 C,所以 C 负责创建(调用构造函数)A,因此 A::member 的值是 88,而不是 5(就像我们创建的对象一样) type B)。

它在解决钻石问题时很有用。:

  A                                        A   A
 / \                                       |   |
B   C                                      B   C
 \ /                                        \ /
  D                                          D
virtual inheritance                   normal inheritance

BC 都继承自 A,而 D 继承自 BC,因此D 中有 2 个 A 的实例! 当你通过 D 访问 A 的成员时,这会导致歧义,因为编译器无法知道你想从哪个类访问该成员(B 继承的那个,或者是 byC 继承的那个?)。

虚拟继承解决了这个问题:由于虚拟基础仅驻留在大多数派生对象中,因此 D 中只有一个 A 实例。

struct A
{
    void foo() {}
};

struct B : public /*virtual*/ A {};
struct C : public /*virtual*/ A {};

struct D : public B, public C
{
    void bar()
    {
        foo(); //Error, which foo? B::foo() or C::foo()? - Ambiguous
    }
};

删除注释可以解决歧义问题。