環境:Ubuntu 12.04 + GCC 4.8.1。本文通過實測內存佈局,揭示虛函數表指針 (vptr) 與虛函數表 (vtable) 在三種繼承模式下的組織規律。
class Base { virtual void f(); virtual void g(); int bval; };
class Derived : public Base { virtual void f(); virtual void h(); int dval; }
佈局:
vptr → 指向 Derived 的 vtable;Base::bval、Derived::dval(按聲明順序);Derived::f、Base::g、Derived::h、nullptr(GCC 特有尾部哨兵);class Base1 { virtual void f(); virtual void g(); int b1; };
class Base2 { virtual void f(); virtual void g(); int b2; };
class Derived : public Base1, public Base2 { virtual void f(); virtual void h(); int d; }
佈局:
Derived::f、Base1::g、Derived::h、哨兵;Derived::f、Base2::g、哨兵;f 在兩個 vtable 中均被覆寫;Base1::f() 否則二義性。class Base { virtual void f(); virtual void t(); int b; };
class Base1 : virtual public Base { virtual void f(); virtual void g(); virtual void k(); int b1; };
class Base2 : virtual public Base { virtual void f(); virtual void g(); virtual void k(); int b2; };
class Derived : public Base1, public Base2 { virtual void f(); virtual void g(); virtual void h(); int d; }
佈局:
Base1 子對象(含 vptr)、Base2 子對象(含 vptr);d;Derived::f、Derived::g、Base1::k、Derived::h、哨兵;Derived::f、Derived::g、Base2::k、哨兵;Derived::f、Base::t、哨兵。| 特性 | 單繼承 | 多重繼承 | 虛繼承 |
|---|---|---|---|
| vptr 數量 | 1 | 每個直接基類 1 個 | 每個直接非虛基類 1 個 + 虛基類 1 個 |
| 基類份數 | 1 | 每個直接基類 1 份 | 虛基類僅 1 份 |
| 虛函數表組織 | 單表,覆寫原地替換 | 每基類一表,派生虛函數放首表 | 非虛基類各自表,虛基類單獨表 |
| 派生虛函數位置 | 首表 | 首表 | 首個非虛基類表 |
理解佈局有助於調試多態調用、分析對象大小、解決切片問題、編寫序列化/反射工具。