C++ 中使用大括號 {} 進行聚合初始化時,類別與結構體行為截然不同。本文深入剖析規則差異、構造函數影響與正確初始化姿勢。
int a[5] = {1,2,3,4,5};
int b[5] = {0}; // 全部歸零
int c[] = {1,2,3,4,5}; // 自動推導長度
struct X { int i; float f; };
X x1 = {1, 1.1f};
X x2[3] = {{1,1.1f}, {2,2.2f}}; // 第三個元素自動歸零
結構體 X 無構造函數、全公開成員 → 聚合類型 → 支援 {} 初始化。
class X {
public:
int i;
float f;
X() { cout << "默認構造\n"; }
X(int e, float q) : i(e), f(q) { cout << "構造函數\n"; }
};
int main() {
// X a = {0, 0}; // 編譯錯誤!有構造函數,非聚合類型
X b[2]; // 調用默認構造函數 ×2
X c[3] = { X(1,1.1f), X(2,2.2f) }; // 顯式調用構造函數
return 0;
}
默認構造函數
默認構造函數
構造函數
構造函數
默認構造函數
c[3] 前兩個顯式構造,第三個隱式默認構造。
| 條件 | 是否為聚合類型 | 支援 {} 初始化 |
|---|---|---|
| 無使用者宣告構造函數、無私有/保護非靜態成員、無基類、無虛函數 | 是 | 是 |
| 有任意使用者宣告構造函數 (含默認、拷貝、移動) | 否 | 否 (C++11 前) |
| 有私有/保護非靜態成員 | 否 | 否 |
| C++11 後:有構造函數但使用 = default | 視情況 | 可能支援 |
現代 C++ 可為類別自定義列表構造函數,恢復 {} 初始化能力:
class Vector {
std::vector<double> data;
public:
Vector(std::initializer_list<double> list) : data(list) {}
// 現在支援 Vector v = {1.0, 2.0, 3.0};
};
struct、全公開、無構造函數 → 享受聚合初始化便利class、私有成員、提供構造函數 → 強制透過構造函數初始化,保證狀態合法std::initializer_list 構造函數X arr[N] = { args... } 缺項自動值初始化 (調用默認構造或歸零)「類別並非嚴格意義上的聚合」。聚合意味著「資料的被動集合」,無封裝、無不變量維護;類別代表「行為與資料的主動封裝」,透過構造函數建立合法初始狀態。這是 C++ 從 C 風格 POD 到面向對象封裝的關鍵分水嶺。