虛擬繼承

使用繼承時,你可以指定 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
    }
};

刪除註釋可以解決歧義問題。