虛基類由最終最派生類直接初始化,而非由中間派生類初始化。這打破了普通繼承「只初始化直接基類」的規則。
#include <iostream>
using namespace std;
class A { // 虛基類
public:
A(int a) : m_a(a) { cout << "A(" << a << ")" << endl; }
protected:
int m_a;
};
class B : virtual public A { // 虛繼承
public:
B(int a, int b) : A(a), m_b(b) { cout << "B(" << a << "," << b << ")" << endl; }
void display() { cout << "m_a=" << m_a << ", m_b=" << m_b << endl; }
protected:
int m_b;
};
class C : virtual public A { // 虛繼承
public:
C(int a, int c) : A(a), m_c(c) { cout << "C(" << a << "," << c << ")" << endl; }
void display() { cout << "m_a=" << m_a << ", m_c=" << m_c << endl; }
protected:
int m_c;
};
class D : public B, public C { // 最終派生類
public:
D(int a, int b, int c, int d) : A(a), B(90, b), C(100, c), m_d(d) {
cout << "D(" << a << "," << b << "," << c << "," << d << ")" << endl;
}
void display() { cout << "m_a=" << m_a << ", m_b=" << m_b << ", m_c=" << m_c << ", m_d=" << m_d << endl; }
private:
int m_d;
};
int main() {
B b(10, 20); b.display();
C c(30, 40); c.display();
D d(50, 60, 70, 80); d.display();
}
B(10,20) // 直接建構 B 時,B 調用 A(10) m_a=10, m_b=20 C(30,40) // 直接建構 C 時,C 調用 A(30) m_a=30, m_c=40 A(50) // ★ D 建構時,最先調用 A(50) —— 虛基類由最派生類初始化 B(90,60) // B 的 A(90) 被忽略 C(100,70) // C 的 A(100) 被忽略 D(50,60,70,80) m_a=50, m_b=60, m_c=70, m_d=80 // m_a 最終為 50,來自 D 直接調用 A(50)
菱形繼承中 B 和 C 共享同一個 A 子物件。若分別初始化,參數衝突(B 傳 90,C 傳 100),編譯器無法決定採用哪個。C++ 規定:由最派生類 D 獨佔初始化權,中間類對虛基類構造函數的調用失效(但語法必須保留)。
無論初始化列表書寫順序,編譯器總是先構造虛基類,再按聲明順序構造其他基類:
// 即使寫成:
D(...) : B(90,b), C(100,c), A(a), m_d(d) { }
// 實際執行順序仍為:
// 1. A(a) ← 虛基類最優先
// 2. B(90,b) ← 按繼承聲明順序
// 3. C(100,c)
// 4. m_d(d)
普通繼承則嚴格按初始化列表順序執行。
| 建構方式 | 虛基類初始化者 | 中間類 A() 調用 |
|---|---|---|
直接建構 B b(10,20) | B (作為最派生類) | 生效 |
直接建構 C c(30,40) | C (作為最派生類) | 生效 |
間接建構 D d(...) | D (最終最派生類) | 失效 (被忽略) |