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

C++ 動態聯編機制深度解析(下):虛函數表佈局、多重繼承與編譯器實現內幕

     編輯:關於C++

前言

承接上篇,本文深入剖析虛函數表 (vtable) 記憶體佈局、指標調整、多重繼承下的 vtable 組織、編譯器靜態/動態階段工作流程,並以圖解方式呈現動態聯編完整執行軌跡。

一、虛函數表 (VTABLE) 核心規則

  1. 每個含虛函數的類別擁有一張 vtable (類別級,非實例級)
  2. 無虛函數的類別無 vtable
  3. 編譯器在類別中植入 vptr (虛函數表指標),指向該類別 vtable
  4. 每個實例建構時初始化 vptr 指向自類別 vtable
  5. vtable 依宣告順序存放虛函數位址;衍生類別未覆寫者繼承基類位址

二、單一繼承 vtable 佈局圖解


// 4.cpp 擴充範例
class shape {
    virtual void draw() { cout << "shape::draw"; }
    virtual void area() { cout << "shape::area"; }
    void fun() { draw(); area(); }
};
class circle : public shape {
    void draw() { cout << "circle::draw"; }
    void adjust() { cout << "circle::adjust"; }
};
class rectangle : public shape { /* 未覆寫 area */ };
class triangle : public shape { /* 未覆寫 adjust */ };

vtable 內容

類別vtable[0]vtable[1]vtable[2]
shapeshape::drawshape::area-
circlecircle::drawshape::areacircle::adjust
rectangleshape::drawrectangle::area-
triangletriangle::drawshape::areatriangle::adjust

物件記憶體佈局

shape 物件:     [vptr] → shape_vtable
circle 物件:    [vptr] → circle_vtable
rectangle 物件: [vptr] → rectangle_vtable

三、編譯器靜態/動態階段工作流程

編譯期 (靜態)

  1. 掃描類別,發現 virtual → 建立 vtable
  2. 將虛函數位址填入 vtable (未覆寫者填基類位址)
  3. 在類別插入 vptr (通常置於物件起始位置)
  4. 不連結虛函數呼叫,保留佔位
  5. 直接連結非虛函數 (靜態聯編)

執行期 (動態)

  1. 建構物件 → 初始化 vptr 指向自類別 vtable
  2. 呼叫虛函數 → 透過 this->vptr[vtable_index] 間接跳轉
  3. this 指向實際物件型別 → 找到正確 vtable → 正確函數位址

四、動態聯編執行軌跡追蹤

案例 1:oneshape.fun() (基類物件)

oneshape (shape) → vptr → shape_vtable
fun() 內部:
  this->draw()  → this->vptr[0] → shape::draw()
  this->area()  → this->vptr[1] → shape::area()

案例 2:baseshape.fun() (基類參考指向衍生物件)

circle circleshape;
shape& baseshape = circleshape;  // 向上轉型
baseshape.fun() 內部:
  this 指向 circleshape 物件
  this->vptr → circle_vtable
  this->draw() → circle_vtable[0] → circle::draw()
  this->area() → circle_vtable[1] → shape::area() (circle 未覆寫)

五、編譯器如何編譯 this->draw()

關鍵洞察:所有衍生類別 vtable 的前 N 個槽位順序與基類完全一致。

  • 基類 shape:vtable[0]=draw, vtable[1]=area
  • 衍生類 circle:vtable[0]=draw, vtable[1]=area, vtable[2]=adjust
  • 編譯器將 this->draw() 編譯為 call this->vptr[0]
  • this->area() 編譯為 call this->vptr[1]
  • 執行時 this 實際型別決定具體 vtable,槽位索引固定 → 正確分派

六、指標型別與虛函數表查找


shape* oneshape = new circle;
oneshape->draw();   // OK: shape_vtable[0] 存在
oneshape->adjust(); // 編譯錯誤: shape_vtable 無 adjust 槽位

circle* c = new circle;
c->adjust();        // OK: circle_vtable[2] 存在

編譯期依靜態型別的 vtable 檢查槽位存在性;執行期依動態型別的 vtable 取實際位址。

七、多重繼承與虛繼承 (補充)

多重繼承下,Base1* p = derived 可能需調整指標值 (加上偏移量),編譯器插入 thunk (調整器) 處理。

八、結語

虛函數表是多型的幕後功臣:編譯期建表、植入 vptr、統一槽位索引;執行期透過 vptr 間接跳轉實現動態分派。理解其佈局規則、指標調整、編譯器靜態/動態分工,是掌握 C++ 物件模型、診斷切面效能、實現動態語言繫結的核心基礎。多重繼承與虛繼承進一步增加了 vptr 數量與指標調整複雜度,屬進階主題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
場景vptr 數量vtable 特徵this 指標調整
單一繼承1線性延伸無
多重繼承 (非虛)≥2各基類各有 vtable,衍生類合併轉型需偏移調整
虛繼承1 (共享虛基類)vbptr 指向虛基類表執行期查 vbtable 調整