核心區別
| 維度 | 動態多態 (Runtime Polymorphism) | 靜態多態 (Compile-time Polymorphism) |
| 實現機制 | 虛函數表 (vtable) + 基類指標/參考 | 模板具現化 + 隱式介面 |
| 綁定時機 | 執行期 | 編譯期 |
| 介面形式 | 顯式介面 (虛函數簽名) | 隱式介面 (有效表達式) |
| 異質容器 | 支援 (基類指標 vector) | 不支援 (模板具現化為具體型別) |
| 效能 | 間接呼叫開銷、難內聯 | 零開銷、編譯器可充分優化 |
| 編譯錯誤 | 清晰 | 複雜冗長 |
| 代碼膨脹 | 無 | 可能 (多次具現化) |
動態多態範例
namespace DynamicPoly {
struct Geometry {
virtual void Draw() const = 0;
virtual ~Geometry() = default;
};
struct Line : Geometry {
void Draw() const override { cout << "Line Draw()\n"; }
};
struct Circle : Geometry {
void Draw() const override { cout << "Circle Draw()\n"; }
};
struct Rectangle : Geometry {
void Draw() const override { cout << "Rectangle Draw()\n"; }
};
void DrawGeometry(const Geometry* g) { g->Draw(); }
// 異質容器 - 動態多態核心優勢
void DrawGeometry(vector vec) {
for (auto g : vec) g->Draw();
}
}
靜態多態範例
namespace StaticPoly {
struct Line {
void Draw() const { cout << "Line Draw()\n"; }
};
struct Circle {
void Draw(const char* = nullptr) const { cout << "Circle Draw()\n"; }
};
struct Rectangle {
void Draw(int = 0) const { cout << "Rectangle Draw()\n"; }
};
template
void DrawGeometry(const Geometry& g) { g.Draw(); }
template
void DrawGeometry(vector vec) {
for (auto& g : vec) g.Draw();
}
}
介面對比
顯式介面 (動態)
void DoSomething(Widget& w) {
if (w.size() > 0 && w != someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
// 需求:Widget 必須提供 size(), operator!=, copy ctor, normalize(), swap()
// 介面在 Widget 類別定義中可見
隱式介面 (靜態)
template
void DoSomething(T& w, const Other& nasty) {
if (w.size() > 0 && w != nasty) { // w.size() 回傳型別只需支援 operator> 與 int 比較
T temp(w); // 需 copy constructible
temp.normalize(); // 需 normalize()
temp.swap(w); // 需 swap(T&)
}
}
// 需求:由有效表達式組成,編譯期具現化時檢查
// 靈活但錯誤訊息難讀
適用場景選擇
- 動態多態:異質物件集合、外掛架構、執行期決定型別、介面穩定
- 靜態多態:高效能泛型演算法、STL 風格、編譯期已知型別、需極致優化
- 混合使用:基類介面動態多態,內部演算法靜態多態 (如 STL 分配器、迭代器)
現代 C++ 趨勢
- C++20 Concepts 為靜態多態引入介面約束,改善錯誤訊息
std::variant + std::visit 提供類動態多態且無繼承耦合
- CRTP (Curiously Recurring Template Pattern) 實現靜態多態的基類複用