承接上篇,本文深入剖析虛函數表 (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[0] | vtable[1] | vtable[2] |
|---|---|---|---|
| shape | shape::draw | shape::area | - |
| circle | circle::draw | shape::area | circle::adjust |
| rectangle | shape::draw | rectangle::area | - |
| triangle | triangle::draw | shape::area | triangle::adjust |
shape 物件: [vptr] → shape_vtable circle 物件: [vptr] → circle_vtable rectangle 物件: [vptr] → rectangle_vtable
virtual → 建立 vtablethis->vptr[vtable_index] 間接跳轉this 指向實際物件型別 → 找到正確 vtable → 正確函數位址oneshape.fun() (基類物件)oneshape (shape) → vptr → shape_vtable fun() 內部: this->draw() → this->vptr[0] → shape::draw() this->area() → this->vptr[1] → shape::area()
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]=areacircle:vtable[0]=draw, vtable[1]=area, vtable[2]=adjustthis->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 取實際位址。
| 場景 | vptr 數量 | vtable 特徵 | this 指標調整 |
|---|---|---|---|
| 單一繼承 | 1 | 線性延伸 | 無 |
| 多重繼承 (非虛) | ≥2 | 各基類各有 vtable,衍生類合併 | 轉型需偏移調整 |
| 虛繼承 | 1 (共享虛基類) | vbptr 指向虛基類表 | 執行期查 vbtable 調整 |