虛函數是 C++ 實現執行期多態的核心機制。透過基類指標/參考指向派生類物件,呼叫虛函數時依物件實際型別動態綁定正確版本。
class Base {
public:
virtual void Display() { cout << "Base"; } // 虛函數
virtual ~Base() {} // 析構函數建議宣告為 virtual
};
class Derived : public Base {
public:
void Display() override { cout << "Derived"; } // 覆寫
};
override (C++11) 確保正確覆寫virtual#includeusing namespace std; class Shape { public: virtual void Draw() { cout << "Shape\n"; } virtual ~Shape() {} }; class Circle : public Shape { public: void Draw() override { cout << "Circle\n"; } }; class Rect : public Shape { public: void Draw() override { cout << "Rectangle\n"; } }; int main() { Shape* shapes[] = {new Shape, new Circle, new Rect}; for (auto s : shapes) s->Draw(); // 輸出: Shape, Circle, Rectangle for (auto s : shapes) delete s; }
若移除 virtual,輸出將全為 Shape(靜態綁定)。
編譯器為每個含虛函數的類別建立虛函數表,表中儲存虛函數位址。每個物件隱含一個 vptr 指向其類別的 vtable。
// 簡化模型
class A {
virtual void Fun1() {}
virtual void Fun2() {}
// 物件記憶體佈局: [vptr] -> vtable_A [&A::Fun1, &A::Fun2]
};
class B : public A {
void Fun1() override {}
// 物件記憶體佈局: [vptr] -> vtable_B [&B::Fun1, &A::Fun2]
};
呼叫 ptr->Fun1() 時:
vptr這解釋了虛函數呼叫的額外開銷(間接跳轉),但換來執行期多態彈性。
class Interface {
public:
virtual void MustImplement() = 0; // 純虛函數
virtual ~Interface() {}
};
#include#include using namespace std; class Base { public: virtual void F1() { cout << "Base::F1\n"; } virtual void F2() { cout << "Base::F2\n"; } }; class Derived : public Base { public: void F1() override { cout << "Derived::F1\n"; } void F2() override { cout << "Derived::F2\n"; } }; int main() { Derived d; Base* p = &d; // 手動模擬虛函數呼叫 (教學用,非標準做法) void** vptr = *reinterpret_cast (&p); typedef void(*FuncPtr)(Base*); FuncPtr f1 = reinterpret_cast (vptr[0]); FuncPtr f2 = reinterpret_cast (vptr[1]); f1(p); // Derived::F1 f2(p); // Derived::F2 }
delete basePtr 僅呼叫基類析構,導致資源洩漏