初始化列表僅指定初始值,**不決定執行順序**。成員初始化順序嚴格遵循類別內宣告順序,與列表書寫順序無關。
class A {
private:
int n1; // 先宣告
int n2; // 後宣告
public:
A() : n2(0), n1(n2 + 2) {} // 列表寫 n2 在前,但 n1 先初始化!
void Print() { std::cout << "n1:" << n1 << ", n2:" << n2 << '\n'; }
};
// 執行結果:n1=隨機值(垃圾值), n2=0
原因:n1 先初始化,此時 n2 尚未初始化(垃圾值),n1 = n2 + 2 變成不可預測值。
class A {
private:
int n1;
int n2;
public:
A() : n1(2), n2(0) {} // 或 A() : n2(0), n1(2) {} 順序無關,但邏輯要對
// 若 n1 依賴 n2,必須將 n2 宣告在 n1 之前
};
| 成員類型 | 原因 | 錯誤示範 | 正確示範 |
|---|---|---|---|
| 無預設建構函式的類別型別成員 | 無法隱式預設建構,必須顯式初始化 | Member m; m = Member(args); | Member m(args); |
| const 成員 | 不可賦值,只能初始化 | const int c; c = 5; | const int c = 5; 或 c(5) |
| 引用成員 | 必須綁定於初始化時,不可重新綁定 | int &r; r = x; | int &r = x; 或 r(x) |
class A {
private:
int i;
const int j;
int &k;
public:
A(int ii) : i(ii), j(i), k(ii) {} // 依宣告順序:i → j → k
A() : j(0), k(i) {} // j=0, k 綁定 i(i 未初始化為 0? 需注意)
};
class A {
int x, y;
const int z;
public:
A(int a, int b, int c) : x(a), y(b), z(c) {}
A(int a) : A(a, 0, 42) {} // 委派給主建構函式
};
-Wreorder (GCC/Clang) / /Wall (MSVC) 會偵測列表順序與宣告順序不符。A(int v) : val{v} {} 防止縮窄轉換。初始化列表不是語法糖,而是 C++ 物件模型的核心機制。掌握「宣告順序決定初始化順序」鐵律,能避免 90% 的建構函式隱性 Bug。