class A { public: virtual void p() { cout << "A" << endl; } };
class B : public A { public: virtual void p() { cout << "B" << endl; } };
int main() {
A* a = new A; A* b = new B;
a->p(); b->p(); // 輸出:A B
delete a; delete b;
}
基類指針指向派生對象,調用虛函數 → 動態綁定,輸出 A B。
A* b = new B; b->p(); 編譯期確定調用 A::p(),輸出 A A。因非虛函數調用在編譯期靜態綁定,忽略對象實際類型。
關鍵認知:
a. 基類指針/引用調用虛函數時,直到運行時才知對象確切類型。
b. 非虛函數:無論實際類型,執行基類版本。
虛函數:運行時根據指針指向的對象類型,調用該類型定義的版本。
故 b->p() 調用 B::p(),輸出 A B。
通用模型:
• 每個含虛函數的類有一個虛表,存放虛函數地址。
• 每個對象含一個vptr,指向其類的虛表。
class Point {
public:
virtual ~Point();
virtual Point& mult(float) = 0; // 純虛
float x() const { return _x; } // 非虛,不進虛表
virtual float y() const { return 0; }
virtual float z() const { return 0; }
protected:
Point(float x = 0.0); float _x;
};
class Point2d : public Point {
public:
Point2d(float x=0, float y=0) : Point(x), _y(y) {}
~Point2d(); // 覆寫
Point2d& mult(float); // 覆寫
float y() const { return _y; } // 覆寫
protected:
float _y;
};
class Point3d : public Point2d {
public:
Point3d(float x=0, float y=0, float z=0) : Point2d(x,y), _z(z) {}
~Point3d();
Point3d& mult(float);
float z() const { return _z; }
protected:
float _z;
};
~Point(slot1), mult(slot2), y(slot3), z(slot4)。Point 對象含 _x + vptr。~Point2d, mult, y;繼承 Point::z。對象含 _y + vptr(指向 Point2d 虛表)。~Point3d, mult, z;繼承 y。
虛函數通過虛表 + vptr 實現運行時多態:構造時設置 vptr 指向正確虛表,調用時通過 vptr 定位槽位跳轉。單繼承下虛表按聲明順序線性佈局,派生類覆寫替換槽位,未覆寫繼承基類槽位。