多態是物件導向程式設計的核心支柱,但在 C++ 中,它的含義遠超單純的虛函數機制。本文系統梳理 C++ 四種多態形式——函數多載、巨集多態、動態多態(虛函數)、靜態多態(模板)——並深入探討動靜態多態的結合策略,幫助你在正確場合選擇最適合的技術。
同一函數名,依參數列表(型別、個數、順序)不同,編譯器透過 Name Mangling 產生不同符號:
int add(int a, int b) { return a + b; }
int add(int a, std::string b) { return a + std::stoi(b); }
// 呼叫 add(1, 2) → add_int_int
// 呼叫 add(1, "2") → add_int_string
優點:型別安全、可讀性高。缺點:僅限編譯期已知型別,無法處理執行期決定的異質物件。
利用前置處理器文字替換,達成「同一記號、不同行為」:
#define ADD(A, B) ((A) + (B))
int i = ADD(1, 2); // 3
std::string s = ADD("Hello, ", "World!"); // "Hello, World!"
缺點:無型別檢查、無作用域、側效應風險大、除錯困難。現代 C++ 應盡量避免,改用內聯函數或模板。
基於繼承與虛函數,透過基類指標/參考操作派生物件:
class Vehicle { public: virtual void run() const = 0; };
class Car : public Vehicle { void run() const override { std::cout << "run a car\n"; } };
class Airplane : public Vehicle { void run() const override { std::cout << "run an airplane\n"; } };
void run_vehicle(const Vehicle* v) { v->run(); } // 執行期分派
void run_vehicles(const std::vector<Vehicle*>& vec) {
for (auto v : vec) v->run(); // 異質集合統一處理
}
優點:支援異質集合、執行期彈性、二進位相容(介面穩定)。
缺點:虛函數表查找開銷、指標間接存取影響快取、物件切割風險、需動態配置記憶體。
透過模板在編譯期為每種具體型別具現化獨立函數:
class Car { public: void run() const { std::cout << "run a car\n"; } };
class Airplane { public: void run() const { std::cout << "run an airplane\n"; } };
template <typename Vehicle>
void run_vehicle(const Vehicle& v) { v.run(); } // 編譯期具現化 run_vehicle<Car>, run_vehicle<Airplane>
template <typename Vehicle>
void run_vehicles(const std::vector<Vehicle>& vec) {
for (const auto& v : vec) v.run(); // 同質集合,零虛函數開銷
}
優點:零執行期開銷、內聯優化機會大、型別安全(編譯期檢查)、物件值語義(無切割)。
缺點:程式碼膨脹、無法直接處理異質集合、編譯期需知所有型別、編譯時間較長。
真實專案常需同時滿足「統一介面處理異質物件」與「特定類型高效能操作」。
run() → 動態多態// 基類階層:動態多態核心
class Vehicle { public: virtual void run() const = 0; virtual ~Vehicle() = default; };
class Car : public Vehicle { void run() const override { /*...*/ } };
class Airplane : public Vehicle {
void run() const override { /*...*/ }
void add_oil() const { std::cout << "add oil to airplane\n"; }
};
class Airship : public Vehicle {
void run() const override { /*...*/ }
void add_oil() const { std::cout << "add oil to airship\n"; }
};
// 動態多態:異質集合統一 run
void run_vehicles(const std::vector<Vehicle*>& vec) {
for (auto v : vec) v->run();
}
// 靜態多態:同質飛行載具集合高效 add_oil
template <typename Aircraft>
void add_oil_to_fleet(const std::vector<Aircraft>& fleet) {
for (const auto& a : fleet) a.add_oil(); // 編譯期內聯,零虛函數開銷
}
// 使用
int main() {
Car c1, c2; Airplane a1, a2; Airship s1, s2;
std::vector<Vehicle*> all = {&c1, &a1, &s1};
run_vehicles(all); // 動態分派
std::vector<Airplane> planes = {a1, a2};
std::vector<Airship> ships = {s1, s2};
add_oil_to_fleet(planes); // 靜態具現化 add_oil_to_fleet<Airplane>
add_oil_to_fleet(ships); // 靜態具現化 add_oil_to_fleet<Airship>
}
| 場景 | 推薦技術 | 理由 |
|---|---|---|
| 異質集合、執行期決定型別、介面穩定需二進位相容 | 動態多態(虛函數) | 單一虛函數表分派,程式碼體積小 |
| 同質集合、編譯期已知型別、極致效能、型別安全 | 靜態多態(模板) | 零開銷、內聯優化、編譯期檢查 |
| 核心介面少數、擴充操作多樣、需同時支援上述兩類需求 | 混合策略 | 動態處理共同介面,靜態處理特定擴充 |
C++ 多態不是非此即彼的選擇,而是工具箱:
掌握四種多態的特性與適用場景,才能寫出兼具效能、安全、可維護的現代 C++ 程式碼。