// 寫法一:建構函數體內賦值
class CSomeClass {
int x, y;
public:
CSomeClass() { x = 0; y = 1; }
};
// 寫法二:成員初始化列表
class CSomeClass {
int x, y;
public:
CSomeClass() : x(0), y(1) { }
}
class CMember {
public:
explicit CMember(int val) : data(val) {} // 唯一建構函數需參數
private:
int data;
};
class CMyClass {
CMember m_member; // 無預設建構函數
public:
CMyClass() : m_member(42) {} // 必須在初始化列表傳參
};
若在建構函數體賦值,編譯器會先嘗試呼叫 CMember() 預設建構函數 → 編譯錯誤。
class CExample {
const int MAX_SIZE;
int& refData;
public:
CExample(int& r) : MAX_SIZE(100), refData(r) {} // 唯一合法位置
};
C++ 規定 const 物件與參考只能初始化,不能賦值。
以 MFC CString 為例:
class CMyClass {
CString m_str;
public:
// 寫法 A:建構函數體賦值 → 呼叫預設建構 + operator=
CMyClass() { m_str = _T("Hello"); }
// 寫法 B:初始化列表 → 直接呼叫 CString(LPCTSTR) 建構函數
CMyClass() : m_str(_T("Hello")) { }
};
CString() 預設建構 → operator=(LPCTSTR) 賦值 = 兩次函數呼叫,可能觸發兩次記憶體配置。CString(LPCTSTR) 直接建構 = 一次函數呼叫,效率更高。對於擁有昂貴建構/賦值操作的大型類別 (配置大量記憶體、開啟檔案控制碼等),初始化列表能顯著減少資源浪費。
對於 int、long、指標等無建構函數的型別,兩種寫法編譯後機器碼幾乎相同,僅有一次移動/賦值指令。此時可依團隊編碼風格選擇。
class CMyClass {
int m_x;
int m_y;
public:
CMyClass(int i) : m_y(i), m_x(m_y) {} // 危險!
};
成員初始化順序遵循類別定義中的宣告順序,而非初始化列表中的書寫順序。上例中 m_x 先初始化,但此時 m_y 尚未初始化 → m_x 得到不定值。
| 情境 | 建議做法 | 理由 |
|---|---|---|
| 成員為類別型別、const、參考 | 必須用初始化列表 | 語法強制要求 |
| 成員為昂貴建構/賦值的類別 (CString, std::vector, 含大量資源的類別) | 優先用初始化列表 | 避免雙重建構/賦值,提升效能 |
| 成員為內建型別、指標、簡單 POD | 任選一致風格 | 無效能差異;建構函數體賦值便於對齊、註釋、memset 等 |
| 多成員相互依賴初始化 | 調整宣告順序 + 列表順序一致 | 確保正確初始化序列 |
class ModernClass {
int x = 0; // C++11 非靜態資料成員初始化
int y = 1;
std::string name; // 預設建構為空字串
public:
ModernClass() = default; // 編譯器自動產生正確建構函數
ModernClass(int a, int b) : x(a), y(b) {} // 仍可覆寫
};
此語法將預設值直接寫在成員宣告處,簡潔且不易出錯,配合 = default 讓編譯器生成最佳建構函數,是現代 C++ 推薦做法。
成員初始化列表非單純語法糖,而是 C++ 物件模型的核心機制。理解「初始化」與「賦值」的本質差異、掌握初始化順序規則、根據成員型別特性選擇恰當策略,是撰寫高效、正確、可維護 C++ 程式碼的關鍵功力。