虛函數是 C++ 運行時多態的核心機制。通過 virtual 關鍵字聲明,配合基類指針/引用指向派生類對象,實現「同一接口,不同行為」。本文從底層實現原理到工程最佳實踐,全方位剖析虛函數體系。
class Base {
public:
virtual void fun() { cout << "Base::fun()\n"; }
};
class Derived : public Base {
public:
void fun() override { cout << "Derived::fun()\n"; } // override 可選但推薦
};
int main() {
Derived d;
Base* pb = &d;
pb->fun(); // 輸出 "Derived::fun()" —— 動態綁定
}
virtual 時:靜態綁定,調用基類版本。virtual 時:動態綁定,通過 虛表 (vtable) 查找實際對象類型對應的函數地址。vtable(函數指針數組)。vptr 指向其類的 vtable。vtable,覆蓋函數則替換對應槽位。pb->fun() 編譯為:(*pb->vptr[slot])(pb)。class A { virtual void f(); };
class B : public A { void f() override; }; // 自動成為虛函數
class C : public B { void f() override; };
C c; B* pb = &c; pb->f(); // 調用 C::f()
規則:基類虛函數 → 所有派生類同名函數自動為虛函數,無論是否寫 virtual。
class Base {
public:
virtual ~Base() { cout << "~Base()\n"; } // 必須虛析構
};
class Derived : public Base {
public:
~Derived() { cout << "~Derived()\n"; }
};
Base* p = new Derived;
delete p; // 輸出 ~Derived() -> ~Base()
virtual 時 delete pb 僅調用 ~Base(),導致派生類資源洩漏。
class Shape {
public:
virtual double area() = 0; // 純虛函數:無實現,強制派生類實現
virtual ~Shape() = default;
};
class Circle : public Shape {
double r;
public:
Circle(double r) : r(r) {}
double area() override { return 3.14159 * r * r; }
};
// Shape s; // 錯誤:抽象類不能實例化
Shape* s = new Circle(1.0); // 正確:指向具體派生類
= 0 僅為語法標記,非賦值操作。| 場景 | 錯誤做法 | 正確做法 |
|---|---|---|
| 構造/析構中調用虛函數 | 動態綁定失效,調用當前類版本 | 避免調用;或用非虛接口模式 (NVI) |
| 普通函數聲明 virtual | 編譯錯誤 | 虛函數只能是非靜態成員函數 |
| 純虛函數提供實現 | 語法合法但罕見 | 通常不提供;若需默認實現可在類外定義 |
| 基類指針 delete 派生對象 | 基類析構非 virtual | 基類析構必須 virtual |
virtual 啟用動態綁定,實現運行時多態。override (C++11) 防止拼寫錯誤導致隱藏而非覆蓋。