在 C++ 多重繼承中,當一個派生類通過多條路徑繼承自同一個基類時,會導致基類成員在派生類中出現多份拷貝,造成二義性。例如經典的「菱形繼承」結構:類 A 是基類,B 和 C 分別繼承 A,D 再繼承 B 和 C。此時 D 會包含兩份 A 的成員,直接訪問會報錯。
解決方案有二:
Base::member)顯式指定。語法:
class Derived : virtual 存取指定符 Base { ... };
關鍵點:
virtual 關鍵字出現在派生類定義中,而非基類。virtual,否則未加者仍會產生獨立副本。規則較一般繼承複雜,核心原則:
class Base1 { public: Base1() { cout << "Base1"; } };
class Base2 { public: Base2() { cout << "Base2"; } };
class Level1 : public Base2, virtual public Base1 { ... };
class Level2 : public Base2, virtual public Base1 { ... };
class TopLevel : public Level1, virtual public Level2 { ... };
建構 TopLevel obj 時輸出順序:
Base1 → Base2 → Level2 → Base2 → Level1 → TopLevel
解析:TopLevel 先建構虛基類 Level2,Level2 又先建構其虛基類 Base1,再建構非虛基類 Base2,最後建構自己。接著 TopLevel 建構非虛基類 Level1,此時 Base1 已建構過跳過,直接建構 Base2 和 Level1,最後建構 TopLevel 自身。
若虛基類有帶參數的建構函式,最底層的派生類必須在初始化列表中直接調用虛基類的建構函式,中間層派生類雖也要在初始化列表中列出,但實際參數以最底層派生類傳遞的為準。
class A { public: A(char c) { ... } };
class B : virtual public A { public: B(char i, char j) : A(i) { ... } };
class C : virtual public A { public: C(char i, char j) : A(i) { ... } }; // 必須寫上 :A(i) 即使不會被使用
class D : public B, public C {
public:
D(char i, char j, char k, char l, char m, char n)
: C(k,l), B(i,j), A(n) { ... } // 這裡的 A(n) 決定最終參數
};
輸出顯示 A 先用 n 建構,其後 B、C 使用各自參數建構,最後 D 自身。注意 C 的初始化列表雖寫 :A(i) 但實際被忽略。