C++ 多型的核心在於晚期繫結,由虛函式、虛函式表 (vtable) 與虛函式指標 (vptr) 共同實現。本文從定義、編譯器實作、記憶體佈局、效能影響與進階應用五個維度深度剖析。
virtual 宣告,衍生類可覆寫 (override),啟用動態派發。thunk 調整 this 指標。vptr (通常位於物件起始位置),指向其類別的 vtable。class Base {
public:
virtual void f() { cout << "Base::f\n"; }
virtual void g() { cout << "Base::g\n"; }
int b;
};
class Derived : public Base {
public:
virtual void f() { cout << "Derived::f\n"; } // 覆寫
virtual void h() { cout << "Derived::h\n"; } // 新增
int d;
};
記憶體圖示:
Base vtable: [ &Base::f ] [ &Base::g ]
Derived vtable: [ &Derived::f ] [ &Base::g ] [ &Derived::h ]
Base object: [ vptr ] -> Base vtable
[ b ]
Derived object: [ vptr ] -> Derived vtable
[ b ] (Base subobject)
[ d ]
Base* ptr = new Derived();
ptr->f(); // 呼叫 Derived::f
// 編譯器產生等效代碼:
// 1. 取得 vptr
void** vptr = *reinterpret_cast(ptr);
// 2. 取得 vtable 中 f 的槽位 (索引 0)
void (*func)() = reinterpret_cast(vptr[0]);
// 3. 呼叫
func(); // 實際執行 Derived::f
| 面向 | 影響 | 緩解策略 |
|---|---|---|
| 間接跳躍 | CPU 分支預測較難,管線停滯 1-3 週期 | 熱點路徑改用 CRTP (靜態多型)、final 關鍵字、link-time optimization |
| 記憶體存取 | 額外一次間接記憶體讀取 (vptr → vtable → func) | 資料局部性佳時影響微小;避免在緊迴圈中虛函式呼叫 |
| 物件大小 | 每實例 +8 bytes (64-bit) vptr | 大量小物件考慮 ECS (Entity Component System) 或值型別設計 |
| 建構/解構 | vptr 多次寫入 | 無額外成本,屬必要操作 |