多态是C++乃至所有面向对象编程语言的核心特性之一。然而,很多同学在实际开发时只会用“虚函数”实现的动态多态,而忽视了C++还支持静态多态。本文将系统梳理多态的定义、官方权威解释、静态与动态多态的区别、典型代码与注意事项,帮助你彻底搞懂C++多态。
多态(Polymorphism),意为“多种形态”。在编程中,它指的是“同一个接口,表现出多种不同的实现方式”。
一句话总结:多态 = 同一接口,不同实现。
来自ISO C++标准文档(如ISO/IEC 14882)对虚函数的描述:
A non-static member function is a virtual function if it is declared with the virtual specifier. If a class has a virtual function, it supports dynamic binding (run-time polymorphism): calls to virtual functions are resolved at run time based on the dynamic type of the object.
翻译:
如果一个非静态成员函数使用了virtual关键字声明,则它是一个虚函数。如果一个类拥有虚函数,则它支持动态绑定(运行时多态):对虚函数的调用在运行时根据对象的动态类型进行解析。
虽然C++标准文档并未专门定义“静态多态”一词,但C++之父Bjarne Stroustrup在《The C++ Programming Language》中明确指出:
C++ supports both static (compile-time) and dynamic (run-time) polymorphism. Static polymorphism is provided by function overloading and class templates. Dynamic polymorphism is provided by virtual functions.
翻译:
C++同时支持静态(编译时)和动态(运行时)多态。静态多态由函数重载和类模板提供。动态多态由虚函数提供。
参考文献:
Bjarne Stroustrup, “The C++ Programming Language” (4th Edition), Section 2.5.4
cppreference - Polymorphism( en.cppreference 商业网/w/cpp/language/polymorphism)
C++多态分为“静态多态”(编译期多态)和“动态多态”(运行期多态)两大类。
静态多态是在编译阶段由编译器决定采用哪种实现的多态,常见方式有:
#include <iostream>
#include <string>
// 定义多个同名函数,参数类型不同
void Print(int a) {
std::cout << "int: " << a << std::endl;
}
void Print(double a) {
std::cout << "double: " << a << std::endl;
}
void Print(const std::string& a) {
std::cout << "string: " << a << std::endl;
}
int main() {
Print(42); // 调用 Print(int)
Print(3.14); // 调用 Print(double)
Print("Hello C++"); // 调用 Print(const std::string&),但这是const char*,会隐式转string
std::string msg = "World";
Print(msg); // 调用 Print(const std::string&)
return 0;
}
说明:
#include <iostream>
#include <string>
// 函数模板,实现通用的交换功能
template<typename T>
void Swap(T& a, T& b) {
T tmp = a;
a = b;
b = tmp;
}
int main() {
int x = 10, y = 20;
Swap(x, y); // 交换两个 int 变量
std::cout << "x = " << x << ", y = " << y << std::endl;
double dx = 1.5, dy = 2.8;
Swap(dx, dy); // 交换两个 double 变量
std::cout << "dx = " << dx << ", dy = " << dy << std::endl;
std::string s1 = "hello", s2 = "world";
Swap(s1, s2); // 交换两个字符串
std::cout << "s1 = " << s1 << ", s2 = " << s2 << std::endl;
return 0;
}
说明:
#include <iostream>
class Point {
public:
int x, y;
Point(int x_, int y_) : x(x_), y(y_) {}
// 重载加号运算符
Point operator+(const Point& rhs) const {
return Point(x + rhs.x, y + rhs.y);
}
// 重载输出流运算符(友元函数)
friend std::ostream& operator<<(std::ostream& os, const Point& pt) {
os << "(" << pt.x << ", " << pt.y << ")";
return os;
}
};
int main() {
Point p1(2, 3);
Point p2(5, 8);
Point p3 = p1 + p2; // 编译时根据参数类型选择 operator+
std::cout << "p1 + p2 = " << p3 << std::endl;
return 0;
}
说明:
#include <iostream>
// 基类模板,T为派生类类型,实现部分通用逻辑
template <typename Derived>
class Base {
public:
void Interface() {
// 编译期间展开为调用派生类实现
static_cast<Derived*>(this)->Implementation();
}
// 可以有其他通用成员函数
};
// 派生类A,实现专属行为
class DerivedA : public Base<DerivedA> {
public:
void Implementation() {
std::cout << "DerivedA implementation\n";
}
};
// 派生类B,实现专属行为
class DerivedB : public Base<DerivedB> {
public:
void Implementation() {
std::cout << "DerivedB implementation\n";
}
};
int main() {
DerivedA a;
DerivedB b;
a.Interface(); // 输出:DerivedA implementation
b.Interface(); // 输出:DerivedB implementation
// 你也可以用模板参数处理不同派生类
Base<DerivedA>* pa = &a;
pa->Interface();
return 0;
}
说明:
动态多态是运行时根据对象的真实类型决定采用哪种实现的多态,C++ 通过继承+虚函数+基类指针/引用实现。
#include <iostream>
#include <vector>
#include <memory>
// 基类:抽象动物
class Animal {
public:
virtual ~Animal() {} // 虚析构,确保通过基类指针安全析构
virtual void Speak() = 0; // 纯虚函数,子类必须实现
};
// 派生类:狗
class Dog : public Animal {
public:
void Speak() override { // override确保签名一致
std::cout << "Woof!" << std::endl;
}
};
// 派生类:猫
class Cat : public Animal {
public:
void Speak() override {
std::cout << "Meow!" << std::endl;
}
};
// 通过基类指针使用多态
void MakeAnimalSpeak(Animal* pAnimal) {
pAnimal->Speak(); // 动态分派,根据实际类型调用
}
int main() {
Dog dog;
Cat cat;
MakeAnimalSpeak(&dog); // 输出:Woof!
MakeAnimalSpeak(&cat); // 输出:Meow!
// 更常见的实际用法:存放指向不同子类的基类指针
std::vector<std::unique_ptr<Animal>> animals;
animals.emplace_back(new Dog());
animals.emplace_back(new Cat());
for (const auto& animal : animals) {
animal->Speak(); // 输出:Woof! Meow!
}
return 0;
}
说明:
| 静态多态(编译期) | 动态多态(运行期) | |
|---|---|---|
| 实现方式 | 函数重载、模板、CRTP | 继承+虚函数 |
| 类型决议 | 编译期间 | 运行期间 |
| 开销 | 无虚表,无运行时开销 | 有虚表,有轻微运行时开销 |
| 应用场景 | 泛型编程、高性能、类型明确 | OOP 抽象、需要运行时类型切换 |
| 依赖继承 | 否 | 是 |
表格控件单元格多态:
#include <iostream>
#include <vector>
#include <memory>
class CGridCell {
public:
virtual ~CGridCell() {}
virtual void Draw() = 0; // 纯虚函数,子类必须实现
};
class CGridCellCombo : public CGridCell {
public:
void Draw() override { std::cout << "[ComboCell] Draw combo box\n"; }
void ShowCombo() { std::cout << "[ComboCell] Show combo\n"; }
};
class CGridCellCheck : public CGridCell {
public:
void Draw() override { std::cout << "[CheckCell] Draw check box\n"; }
void ToggleCheck() { std::cout << "[CheckCell] Toggle check state\n"; }
};
void DrawAllCells(const std::vector<CGridCell*>& cells) {
for (auto cell : cells) {
cell->Draw(); // 多态调用
}
}
int main() {
CGridCellCombo comboCell;
CGridCellCheck checkCell;
std::vector<CGridCell*> grid = { &comboCell, &checkCell };
DrawAllCells(grid);
return 0;
}
说明:
如果需要判断实际类型,常见做法:
C++ 提供了 dynamic_cast,可以安全判断指针实际类型,前提是基类有虚函数(通常有虚析构函数即可)。
CGridCell* pCell = ...;
if (auto pCombo = dynamic_cast<CGridCellCombo*>(pCell)) {
// 是 CGridCellCombo 类型
pCombo->ShowCombo();
} else if (auto pCheck = dynamic_cast<CGridCellCheck*>(pCell)) {
// 是 CGridCellCheck 类型
pCheck->ToggleCheck();
} else {
// 其它类型
}
注意事项:
如果类型判断很频繁,或者需要遍历大批数据时,为提升性能可用自定义类型码:
enum GridCellType {
Cell_Normal,
Cell_Combo,
Cell_Check,
// ...
};
class CGridCell {
public:
virtual ~CGridCell() {}
virtual GridCellType GetCellType() const { return Cell_Normal; }
};
class CGridCellCombo : public CGridCell {
public:
GridCellType GetCellType() const override { return Cell_Combo; }
};
class CGridCellCheck : public CGridCell {
public:
GridCellType GetCellType() const override { return Cell_Check; }
};
// 用法
switch (pCell->GetCellType()) {
case Cell_Combo:
static_cast<CGridCellCombo*>(pCell)->ShowCombo();
break;
case Cell_Check:
static_cast<CGridCellCheck*>(pCell)->ToggleCheck();
break;
}
适用场景:
typeid 也可以获取类型名,但很少用在实际开发(几乎只用来调试)。因为返回的是 type_info,没法直接拿来做分支判断,效率和跨编译器一致性也差。
typeid 是 C++ 的运行时类型识别(RTTI)工具,可以获取变量的类型信息(返回 type_info 对象)。
#include <iostream>
#include <typeinfo>
class Base { public: virtual ~Base() {} };
class Derived : public Base {};
int main() {
Base* p = new Derived();
// 运行时获得类型信息
std::cout << typeid(*p).name() << std::endl;
int n = 10;
std::cout << typeid(n).name() << std::endl;
delete p;
return 0;
}
if (typeid(*p) == typeid(Derived)) {
// p 实际类型是 Derived
}
class Base {
public:
virtual ~Base() {}
};
Derived d;
Base b = d; // 对象切片,只剩下Base部分
C++ supports both static (compile-time) and dynamic (run-time) polymorphism. Static polymorphism is provided by function overloading and class templates. Dynamic polymorphism is provided by virtual functions.
—— Bjarne Stroustrup, The C++ Programming Language, 4th Edition, Section 2.5.4
Polymorphism is the ability to treat a derived class object as if it were a base class object. In C++, this is supported by virtual functions and pointers/references to base classes. Static polymorphism is achieved through function and operator overloading, as well as templates.

