C++ 類的記憶體佈局直接影響物件大小、效能與多態行為。本文通過系統性測試,揭示 sizeof 在各種繼承、虛函數、靜態成員組合下的具體數值與底層原理,包含虛表指標 (vptr)、虛基類指標、空類優化、記憶體對齊等核心機制。
int 4 bytes| 類別定義 | sizeof | 解析 |
|---|---|---|
class CEmptyClass {}; | 1 | 空類大小非 0,確保每個物件有唯一位址 |
class A { virtual void a(); }; | 4 | 含虛函數 → 編譯器注入 vptr (4 bytes) |
class A2 { static int m_sNum; }; | 1 | 靜態成員不佔物件空間,屬於類別而非實例 |
class A3 { virtual void a(); static int m_sNum; }; | 4 | vptr 4 bytes,靜態成員不計入 |
class A4 { bool m_b; int m_n; static int m_s; }; | 8 | bool(1) + padding(3) + int(4) = 8 (對齊) |
| 繼承方式 | 定義 | sizeof | 解析 |
|---|---|---|---|
| 非虛繼承 | class B : public A {}; | 4 | 共享基類 vptr,無額外開銷 |
| 虛繼承 | class B1 : virtual public A {}; | 8 | +4 bytes:虛基類指標 (vbptr 或偏移量) |
結論:虛繼承比非虛繼承多 4 bytes (32位元),本質是額外的指標用於執行期解析虛基類位址。
| 定義 | sizeof | 解析 |
|---|---|---|
class B2 : public A, public A1 {}; (雙虛函數基類) | 4 | 單一 vptr,多重繼承不增加虛表指標 |
class B3 : public A1, public A2 {}; (一虛一非虛) | 4 | 僅虛函數基類貢獻 vptr |
關鍵發現:一個類別只有一個虛表,一個物件只有一個 vptr,與繼承多少基類無關。
| 定義 | sizeof | 解析 |
|---|---|---|
class B4 : public A, virtual public A1 {}; | 12 | A(4) + A1(4) + vbptr(4) |
class B5 : virtual public A, virtual public A1 {}; | 12 | 同前,虛繼承只增加單一 vbptr |
結論:無論單一或多重虛繼承,只增加一個虛基類指標 (4 bytes)。
class A { virtual void a(); }; // sizeof=4
class B1 : virtual public A {}; // sizeof=8 (vptr + vbptr)
class C2 : public B1, virtual public A {}; // sizeof=8
class C3 : public B1 {}; // sizeof=8
class C4 : virtual public B1 {}; // sizeof=12
class C5 : virtual public B1, virtual public A {}; // sizeof=12
| 類別 | sizeof | 關鍵機制 |
|---|---|---|
| C2 | 8 | 虛基類共享:virtual public A 被 B1 已虛繼承,C2 再次虛繼承 A 不增加空間 |
| C3 | 8 | 非虛繼承 B1,B1 已含虛基類機制,直接複用 |
| C4 | 12 | 虛繼承 B1 → 新增 vbptr 指向 B1 的虛基類表 |
| C5 | 12 | 雙重虛繼承但 A 已被 B1 虛繼承,共享機制生效,等同 C4 |
A a1, a2;
if (&a1 == &a2) // 錯誤寫法:比較物件位址
// 正確驗證:取得 vptr 位址 (編譯器相關)
int* vptr1 = *reinterpret_cast(&a1);
int* vptr2 = *reinterpret_cast(&a2);
assert(vptr1 == vptr2); // 同類別物件共享虛表指標值
鐵律:同一類別的所有物件,vptr 指向同一虛表。虛表屬於類別,不屬於物件。
bool + int 因對齊佔 8 bytes,重排欄位可省空間熟練掌握類大小計算規則,是 C++ 物件模型、效能優化、序列化、跨語言互操作的核心功力。