虛函數表(vtable)是 C++ 實現運行時多態的核心機制。本文通過代碼實測、內存圖解,系統講解 vtable 結構、繼承與覆蓋規則、多重繼承佈局、安全性風險,基於 32 位 Windows XP + VS2003 / Linux GCC 4.1.3 環境。
class Base { virtual void f(); virtual void g(); virtual void h(); };
Base b;
typedef void(*Fun)();
Fun pFun = (Fun)*((int*)*(int*)(&b)); // 取 vptr → vtable[0] → f()
pFun(); // 調用 Base::f
內存圖:
對象地址 → vptr → vtable → [f(), g(), h(), NULL/1]
vtable 末尾標誌:VC++ 用 NULL,GCC 用 1(有下一個 vtable)/0(最後)。
多態本質:Base* b = new Derive(); b->f() 通過 vptr 找到 Derive::f 地址並調用。
class Base1 { virtual void f(); g(); h(); };
class Base2 { virtual void f(); g(); h(); };
class Base3 { virtual void f(); g(); h(); };
class Derive : public Base1, public Base2, public Base3 { virtual void f(); g1(); };
無覆蓋:每個基類獨立 vtable,子類函數放入第一個基類(Base1) vtable 末尾。
有覆蓋:所有基類 vtable 中的同名虛函數均被替換為派生類版本。確保任意基類指針指向派生對象時調用正確覆蓋函數。
編譯期阻止 b1->g1(),但運行期可通過 vtable 索引調用:
pFun = (Fun)pVtab[0][3]; // Derive::g1 位於 Base1 vtable 第 4 位
pFun(); // 成功調用
class Base { private: virtual void f() { cout << "Base::f"; } };
class Derive : public Base {};
Derive d;
((Fun)*((int*)*(int*)(&d)))(); // 直接調用 private Base::f
vtable 存儲函數地址,不保存訪問權限,運行期無權限檢查。
Debug 模式下展開對象實例,可查看 __vfptr 指向的 vtable 內容(不完整)。
vtable 是 C++ 多態的「地圖」:單繼承單表、多重繼承多表、覆蓋替換同位、訪問控制僅編譯期有效。理解其佈局有助於調試懸空指針、分析二進制兼容性、設計插件系統。記住:所有示例基於 32 位環境,64 位指針寬度翻倍,佈局邏輯不變。