C++ 動態綁定(多態)通過 虛函數 實現,但 必須通過指針或引用調用 才能觸發。直接用對象調用會發生「對象切片」,退化為靜態綁定。
#include <iostream>
using namespace std;
const double pi = 3.14;
class A {
public:
A() {}
virtual double area() const { return 0.0; }
};
class B : public A {
public:
B(double r) : R(r) {}
virtual double area() const { return pi * R * R; }
private:
double R;
};
int main() {
A* fig;
B c(3.0);
cout << "Area of B is ";
fig = &c; // 基類指針指向派生對象
cout << fig->area() << endl; // 動態綁定:調用 B::area()
return 0;
}
// 輸出:Area of B is 28.26
int main() {
A fig; // 基類對象
B c(3.0);
cout << "Area of B is ";
fig = c; // 對象切片!只複製 A 部分
cout << fig.area() << endl; // 靜態綁定:調用 A::area()
return 0;
}
// 輸出:Area of B is 0
| 調用方式 | 靜態類型 | 動態類型 | 綁定類型 | 結果 |
|---|---|---|---|---|
A* p = &b; p->f() | A* | B* | 動態 | B::f() |
A& r = b; r.f() | A& | B | 動態 | B::f() |
A a = b; a.f() | A | A | 靜態 | A::f() |
b.f() | B | B | 靜態(但也是動態) | B::f() |
對象切片本質:A a = b; 僅拷貝 b 中繼承自 A 的成員(vptr 指向 A 的虛表),B 獨有成員(如 R)丟失,vptr 不變,故調用 A::area()。
A 對象內存佈局: B 對象內存佈局:
+----------+ +----------+
| vptr ──────────→ A's vtable | vptr ──────────→ B's vtable
+----------+ +----------+
| R (double) |
+----------+
A's vtable: B's vtable:
[0] &A::area() [0] &B::area()
delete base_ptr 僅調用基類析構virtual void f() = 0; 定義接口,強制派生類實現class Base {
public:
Base() { init(); } // 危險!
virtual void init() { cout << "Base init\n"; }
};
class Derived : public Base {
public:
Derived() { /* ... */ }
void init() override { cout << "Derived init\n"; } // 不會被調用!
};
規則:構造函數中動態類型尚未完全形成,虛函數調用解析為當前構造層級的版本。析構函數同理(派生部分已銷毀)。
理解動態綁定機制與對象切片是 C++ 面向對象編程的分水嶺,也是面試高頻考點。