C++ 對象模型是理解多態、繼承、性能優化的基石。本文系統剖析各類繼承場景下的對象內存佈局,含虛函數表指針、成員變量排列、虛繼承 vbptr/vtable 機制,基於 VC++ 2003 與 G++ 3.4.4 實測。
不討論編譯器優化與字節對齊,聚焦語言語義層面。
class Parent { int iparent; virtual void f(); virtual void g(); virtual void h(); };
class Child : public Parent { int ichild; virtual void f(); virtual void g_child(); };
class GrandChild : public Child { int igrandchild; virtual void f(); virtual void g_child(); };
佈局(VC++/G++ 一致):
[0] vptr → GrandChild::f, Parent::g, Parent::h, GrandChild::g_child, Child::h_child, GrandChild::h_grandchild [1] Parent::iparent [2] Child::ichild [3] GrandChild::igrandchild
class Base1 { int ib1; virtual void f(); g(); h(); };
class Base2 { int ib2; virtual void f(); g(); h(); };
class Base3 { int ib3; virtual void f(); g(); h(); };
class Derive : public Base1, public Base2, public Base3 { int id; virtual void f(); g1(); };
佈局:
[0] Base1::vptr → Derive::f, Base1::g, Base1::h, Derive::g1, nullptr [1] Base1::ib1 [2] Base2::vptr → Derive::f, Base2::g, Base2::h, nullptr [3] Base2::ib2 [4] Base3::vptr → Derive::f, Base3::g, Base3::h, nullptr [5] Base3::ib3 [6] Derive::id
class B { int ib; char cb; virtual void f(); Bf(); };
class B1 : public B { int ib1; char cb1; virtual void f(); f1(); Bf1(); };
class B2 : public B { int ib2; char cb2; virtual void f(); f2(); Bf2(); };
class D : public B1, public B2 { int id; char cd; virtual void f(); f1(); f2(); Df(); };
佈局關鍵:D 包含兩份 B 子對象(分別來自 B1、B2),導致數據冗餘與二義性:
D::B1::vptr → D::f, B1::Bf1, D::f1, D::Df, nullptr B::ib, B::cb, B1::ib1, B1::cb1 D::B2::vptr → D::f, B2::Bf2, D::f2, nullptr B::ib, B::cb, B2::ib2, B2::cb2 D::id, D::cd
訪問 d.ib 報二義性錯誤,需顯式指定 d.B1::ib 或 d.B2::ib。
class B1 : virtual public B { ... };
class B2 : virtual public B { ... };
class D : public B1, public B2 { ... };
VC++ 佈局:
[0] D::B1::vptr [1] B1::ib1, B1::cb1 [2] D::B2::vptr [3] B2::ib2, B2::cb2 [4] D::id, D::cd [5] NULL 分隔符 [6] B::vptr (共享基類) [7] B::ib, B::cb
B 只有一份實例,放在最末尾NULL 分隔;GCC 通過偏移量計算定位
typedef void(*Fun)();
int** pVtab = (int**)&obj;
for (int i=0; (Fun)pVtab[0][i]!=NULL; i++) {
((Fun)pVtab[0][i])(); // 調用虛函數
}
C++ 對象模型精妙複雜:單繼承線性、多重獨立 vptr、重復冗餘、虛繼承共享延後。理解佈局有助於調試多態異常、優化內存、設計 ABI 兼容庫。現代 C++ 開發雖少直接操作內存,但「知其然更知其所以然」是高手必修課。