在 C++ 中,實現運行時多態(動態聯編)是物件導向程式設計的核心機制之一。要達成此目標,必須滿足三個條件:類別之間存在繼承關係;基類與派生類的成員函數被宣告為 virtual(虛函數);透過基類的指標或參考來操作派生類物件(滿足賦值相容性規則)。虛函數的引入,正是為了解決「在編譯期無法確定具體調用哪個函數」的問題,從而實現動態綁定。
靜態聯編意味著編譯器在編譯階段就能將函數呼叫解析為具體的機器碼跳轉指令。每個函數擁有唯一的物理位址,編譯器直接將呼叫點替換為 JMP 或 CALL 該位址的指令。這效率極高,但缺乏彈性——所有呼叫關係必須在編譯期確定。
當編譯期無法確定具體調用哪個函數(例如基類指標指向派生類物件)時,必須採用動態聯編。程式在運行時根據物件的實際類型查找並調用對應的函數。這正是虛函數的用武之地。
C++ 編譯器通過 虛函數表(Virtual Table, vtable) 來實現動態綁定。這是一張函數位址查找表,每個擁有虛函數的類別(或其實例)都關聯一張這樣的表。
__vptr),指向其類別的虛函數表。多重繼承物件包含多個 vptr,分別指向各基類子物件對應的虛表。thiscall 調用。class Base { virtual void f(); };
class Derived : public Base { void f() override; };
// Base 物件佈局:[vptr] -> Base::vtable [ &Base::f ]
// Derived 物件佈局:[vptr] -> Derived::vtable [ &Derived::f ]
當透過 Base* ptr = new Derived(); ptr->f(); 調用時:
ptr->vptr 獲取虛表位址。f 的固定索引(如 0)計算偏移。Derived::f 位址並調用。多重繼承下,派生類物件包含多個基類子物件,各自擁有 vptr。調用不同基類介面的虛函數時,編譯器會自動調整 this 指標(thunk 機制)以指向正確的子物件起始位置。虛繼承則引入了虛基類表指標(vbptr),進一步增加了間接層級。
虛函數表機制是 C++ 實現運行時多態的基石。理解 vtable、vptr 及索引的協作原理,不僅有助於深入掌握物件模型,更能在效能調優、跨語言互操作(如 COM)、逆向工程等場景中發揮關鍵作用。