虛擬繼承
使用繼承時,你可以指定 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
B
和 C
都繼承自 A
,而 D
繼承自 B
和 C
,因此在 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
}
};
刪除註釋可以解決歧義問題。