C++ 標準僅規定語義行為,**不強制記憶體佈局**。但主流編譯器 (MSVC, GCC, Clang, ICC) 遵循 Itanium C++ ABI (非 Windows) 或 MSVC ABI (Windows),形成事實標準。
alignas/#pragma pack 影響對齊。class Base { virtual void f(); int a; };
class Derived : public Base { virtual void f(); int b; };
// 記憶體佈局 (MSVC x64)
Derived:
+0 vptr ──────► Derived::vtable [ &Derived::f, ... ]
+8 Base::a
+12 Derived::b
// 大小: 16 (含對齊)
class Base1 { virtual void f1(); int a; };
class Base2 { virtual void f2(); int b; };
class Derived : public Base1, public Base2 { virtual void f1(); int c; };
// 佈局
Derived:
+0 vptr1 ──────► Derived::vtable_for_Base1 [ &Derived::f1, ... ]
+8 Base1::a
+16 vptr2 ──────► Derived::vtable_for_Base2 [ &Derived::f2, ... ] // 注意:此 vtable 可能共用或獨立
+24 Base2::b
+32 Derived::c
Base2* p2 = static_cast(pDerived) → 指標偏移 +16 (this pointer adjustment)。class Base { virtual void f(); int a; };
class Derived1 : virtual public Base { int b; };
class Derived2 : virtual public Base { int c; };
class Final : public Derived1, public Derived2 { int d; };
// 佈局 (簡化)
Final:
+0 vptr1 ──────► vtable for Derived1
+8 Derived1::b
+16 vptr2 ──────► vtable for Derived2
+24 Derived2::c
+32 vbptr ──────► Virtual Base Table (vbtable) → 指向共享 Base 子物件偏移
+40 Final::d
+48 Base::a (共享單一實例)
+56 Base::vptr (或由 Final 的 vtable 統一管理)
sizeof(Empty) == 1:確保不同物件擁有不同位址。class Derived : Empty { int x; }; → sizeof(Derived) == sizeof(int)。空基類不佔用空間(前提:無虛函數、非虛繼承)。/d1reportSingleClassLayout<ClassName>cl /d1reportSingleClassLayoutDerived test.cpp
// 輸出詳細佈局:偏移、大小、vptr/vbptr 位置、基類子物件結構
-fdump-class-hierarchy / -fdump-lang-classg++ -fdump-class-hierarchy test.cpp
# 生成 test.cpp.001t.class 文件,含完整類別層次與佈局
Derived d;
std::cout << "Size: " << sizeof(d) << std::endl;
std::cout << "vptr offset: " << (char*)&d - (char*)*(void**)&d << std::endl; // 簡易驗證
| 場景 | 影響 | 對策 |
|---|---|---|
| 跨 DLL 傳遞 C++ 物件 | ABI 不兼容 (vtable 位址、佈局差異) | 純 C 介面 / COM / 統一編譯器版本與標準庫 |
| 序列化/網路傳輸 | 指標、vptr、填充位元不可直接複製 | POD 結構體 / 显式序列化 (Protobuf, FlatBuffers, cereal) |
| 效能優化 | 繼承深度、虛函數調用間接開銷、緩存行未命中 | 組合優於繼承、熱點路徑 final/CRTP 消除虛調用、數據導向設計 (DOD) |
| 逆向工程/安全 | vtable 位址洩漏類別資訊、RTTI 洩漏名稱 | 編譯器選項隱藏符號、控制流完整性 (CFI)、去除 RTTI (-fno-rtti) |
C++ 物件模型核心:**非靜態成員按序排列 + 虛函數引入 vptr/vtable + 繼承引入子物件組合與指標調整 + 虛繼承引入 vbptr/vbtable 共享單一基類**。理解佈局,才能寫出高效、可移植、安全的系統級代碼。