虛函數是 C++ 多態的核心。本文通過 VS 調試器圖解,從單繼承到多重繼承、虛基類,系統剖析對象佈局、vptr 指向、vtable 共享、覆蓋替換、指針調整等細節,並給出 C 語言手動模擬完整代碼。
vptr (void**),指向該類的唯一靜態 vtable(函數指針數組)。// 基類 1(有虛函數)
struct CBase1 { void** __vfptr; int base1_1; };
struct CBase1_VFTable { void (__stdcall* base1_fun1)(CBase1*); };
// 基類 2(無虛函數)
struct CBase2 { int base2_1; };
// 基類 3(有虛函數)
struct CBase3 { void** __vfptr; int base3_1; };
struct CBase3_VFTable { void (__stdcall* base3_fun1)(CBase3*); };
// 派生類:繼承順序 Base1, Base2, Base3
struct CDerive1 {
CBase1 base1;
CBase3 base3; // 有虛函數的基類優先靠前
CBase2 base2;
int derive1_1;
};
// 合併 vtable:Base1 表尾部追加派生類虛函數
struct CBase1_CDerive1_VFTable {
void (__stdcall* base1_fun1)(CBase1*);
void (__stdcall* derive1_fun1)(CDerive1*);
};
// Base3 覆蓋表
struct CBase3_CDerive1_VFTable {
void (__stdcall* base3_fun1)(CDerive1*);
};
// 全局 vtable 實例
CBase1_VFTable __vftable_base1 = { base1_fun1 };
CBase3_VFTable __vftable_base3 = { base3_fun1 };
CBase1_CDerive1_VFTable __vftable_base1_derive1 = { base1_fun1, derive1_fun1 };
CBase3_CDerive1_VFTable __vftable_base3_derive1 = { base3_derive1_fun1 };
// 構造派生對象
CDerive1 d1;
d1.base1.base1_1 = 11;
d1.base1.__vfptr = (void**)&__vftable_base1_derive1; // 指向合併表
d1.base3.base3_1 = 31;
d1.base3.__vfptr = (void**)&__vftable_base3_derive1; // 指向覆蓋表
d1.base2.base2_1 = 21;
d1.derive1_1 = 1;
// 手動計算基類指針偏移
char* p = (char*)&d1;
CBase1* pb1 = (CBase1*)(p + 0);
CBase3* pb3 = (CBase3*)(p + sizeof(CBase1));
CBase2* pb2 = (CBase2*)(p + sizeof(CBase1) + sizeof(CBase3));
CDerive1* pd1 = (CDerive1*)p;
// 調用:(*pb3->__vfptr[0])(pb3) 實現多態
理解 vtable 機制有助於調試多態問題、優化性能、實現跨語言互操作。C++11 後仍沿用此 ABI(Itanium/MSVC)。