首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 虛函數表 (vtable) 佈局深度解析:單繼承/多重繼承/多層繼承/虛基類

     編輯:關於C++

實驗工具:顯示 vtable 內容

void DispVFT(DWORD* pVFT) {
    printf("VFT Pointer:%p\n", pVFT);
    DWORD* p = (DWORD*)*pVFT; // 取 vtable 首址
    while (*p) {
        printf("VF:%p , %p\n", p, *p);
        ++p;
    }
}

案例 1:單一類別

class C1 { virtual void F1() {} };
// 輸出: 1 個 vptr, vtable 含 C1::F1

案例 2:單繼承 (C2 : C1)

  • **單一 vptr**,指向**單一 vtable**。
  • vtable 內容:基類虛函數在前,衍生類新增/覆寫虛函數在後。
  • `C1* p = &c2;` 直接使用該 vptr,無偏移。

案例 3:多重繼承 (C3 : C1, C2)

  • **兩個 vptr** (對應兩個直接基類)。
  • **三個 vtable**:
    1. C1 子物件 vtable:含 C1::F1, C3::F3
    2. C2 子物件 vtable:含 C2::F2, C1::F1, C3::F3 (含基類條目)
    3. C3 完整 vtable (主 vtable,通常與第一個基類合併)
  • 轉型 `C2* p = &c3;` 需調整 `this` 指標偏移 (指向 C2 子物件)。

案例 4:多層繼承 (C3 : C2 : C1)

  • **單一 vptr**,但**建構期間產生多個臨時 vtable** (每層建構函數設置自己的 vptr)。
  • 最終僅保留最衍生類 (C3) 的 vtable,包含所有虛函數。
  • **空間浪費**:中間層 vtable 佔用記憶體且僅建構期使用。

優化:`__declspec(novtable)`

  • 標記**抽象基類** (不直接實例化) 為 `novtable`,編譯器**不生成**該類別的 vtable,也不在建構函數中設置 vptr。
  • 最終僅最衍生類生成 vtable,節省空間與建構開銷。
  • **禁忌**:若基類被直接實例化 (如 `C2 c2;`) 或建構函數中呼叫虛函數,使用 `novtable` 會導致崩潰。

虛基類 (Virtual Inheritance) 補充

  • 虛基類引入 **vbptr (虛基類表指標)**,指向 **vbtable**,記錄虛基類偏移量。
  • 最衍生類負責初始化虛基類,vptr/vbptr 佈局更複雜 (通常 vptr 在前,vbptr 緊隨)。
  • 多重繼承 + 虛基類 = **菱形繼承** 解決方案,但顯著增加物件大小與轉型開銷。

總結表

繼承形式vptr 數量vtable 數量this 指標調整novtable 適用
單一類別11無否 (可實例化)
單繼承11無抽象基類可用
多重繼承基類個數基類個數 + 1需偏移抽象基類可用
多層繼承1層數 (建構期) → 1 (最終)無中間抽象層可用
虛繼承1 (+ vbptr)複雜需 vbtable 查詢視情況

實務啟示

  • **單繼承零開銷** (單 vptr, 無偏移)。
  • **多重繼承謹慎使用**,介面類 (Pure Virtual) 採介面隔離原則,實作繼承優先組合。
  • **抽象基類加 `novtable`** (MSVC) 或 `-fno-rtti` 配合純虛介面減輕開銷。
  • **虛函數非免費**:間接跳轉、指令快取未命中、阻礙內聯。熱路徑考慮 CRTP/靜態多態。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved