靜態綁定(早綁定):編譯期確定調用哪個函數。
- 非虛函數、函數重載、模板
- 父類指針指向子類對象,調用非虛函數 → 執行父類版本
動態綁定(晚綁定):運行期根據對象實際類型確定調用。
- virtual 虛函數
- 父類指針/引用指向子類對象,調用虛函數 → 執行子類版本
每個包含虛函數的類,編譯器生成一個虛函數表(靜態數組),存儲虛函數地址。
每個對象實例內部隱含一個虛指針,指向其類的 vtable。
class Base {
int a; // 4 bytes
virtual void f(); // 引入 vptr (4/8 bytes)
}; // sizeof(Base) = 8 (32位) 或 16 (64位)
class Derived : public Base {
int b; // 4 bytes
virtual void f(); // 覆寫,vtable 更新
}; // sizeof(Derived) = 12 (32位) 或 24 (64位)
// 內存圖:
// Base 對象: [vptr] -> Base::vtable -> [Base::f]
// [a]
// Derived 對象:[vptr] -> Derived::vtable -> [Derived::f]
// [a]
// [b]
vptr 找到 vtablevtable 中按固定槽位找到函數地址class Base { public: int a; virtual void f() { cout << "Base"; } };
class Derived : public Base { public: int b; virtual void f() { cout << "Derived"; } };
int main() {
Base b; Derived d;
Base* p = &d;
p->f(); // 輸出 "Derived" - 動態綁定
// 內存探測
int* pb = (int*)&b;
int* pd = (int*)&d;
cout << *pb; // vptr 地址
cout << *(pb+1); // a
int* vtable_b = (int*)*pb;
cout << *vtable_b; // Base::f 地址
}
virtualclass Base { public: virtual ~Base() {} }; // 必須虛析構!
class Derived : public Base { public: ~Derived() { delete[] ptr; } };
Base* p = new Derived();
delete p; // 正確調用 Derived::~Derived() 再 Base::~Base()
若基類析構非虛,delete 基類指針只調用基類析構 → 派生類資源洩漏。
class Shape {
public:
virtual double area() = 0; // 純虛函數
virtual ~Shape() {}
}; // Shape 為抽象類,不可實例化
C++ 多態通過 vptr + vtable 實現動態綁定,空間換時間(每對象額外一指針),支撐面向對象的核心特性。理解內存佈局有助於調試、性能分析和避免對象切片等陷阱。