C++ 运行时多态 vs 编译时多态!
核心一句话
编译时多态:编译器在编译阶段就确定调用哪个函数(静态) 运行时多态:程序运行时才确定调用哪个函数(动态)
一、编译时多态(静态多态)
是什么?
编译期间就确定调用哪个函数,不需要等到运行。
实现方式
- 函数重载(同一个函数名,不同参数)
- 运算符重载
- 模板(泛型)
例子:函数重载
cpp
运行
class Test {
public:
void show(int a) { // 版本1
cout << "int:" << a;
}
void show(string a) { // 版本2
cout << "string:" << a;
}
};
Test t;
t.show(10); // 编译期就确定调用第1个
t.show("abc"); // 编译期就确定调用第2个
特点
- 早绑定(early binding)
- 速度快
- 无虚函数,无额外开销
- 编译期就确定调用目标
二、运行时多态(动态多态)
是什么?
运行起来之后,才知道调用子类还是父类的函数。
实现条件
- 继承
- 父类有虚函数(virtual)
- 父类指针 / 引用指向子类对象
- 通过指针调用虚函数
例子
cpp
运行
class Animal {
public:
virtual void speak() { // 必须 virtual
cout << "动物叫" << endl;
}
};
class Dog : public Animal {
public:
void speak() override {
cout << "汪汪汪" << endl;
}
};
Animal* a = new Dog();
a->speak(); // 运行时才知道调用 Dog::speak()
特点
- 晚绑定 / 动态绑定(late binding)
- 依赖 虚函数表
- 可以灵活扩展,符合开闭原则
- 有一点点性能开销
三、最核心区别(面试必背)
表格
| 编译时多态 | 运行时多态 | |
|---|---|---|
| 确定时机 | 编译时 | 运行时 |
| 实现方式 | 重载、模板 | 继承 + 虚函数 |
| 绑定方式 | 早绑定 | 晚绑定 |
| 效率 | 快 | 稍慢 |
| 灵活性 | 低 | 高 |
| 是否需要 virtual | 不需要 | 必须需要 |
四、使用场景(怎么选?)
✔ 编译时多态使用场景
- 功能相同,参数不同 → 用重载
- 通用逻辑,多种类型都能用 → 用模板
- 追求速度、底层、高性能
例子:
- 工具函数
- 数据结构(vector、list)
- 算法函数
✔ 运行时多态使用场景
- 父类指针指向不同子类
- 需要统一接口、不同实现
- 框架、插件、模块化设计
- Qt 上位机大量使用!
Qt 真实场景(你项目必用)
cpp
运行
QWidget* w = new QPushButton;
w->show(); // 运行时多态
w = new QLabel;
w->show(); // 自动调用对应控件的show
所有 Qt 控件都靠运行时多态实现!
五、最简单总结(背会这 3 句)
- 编译时多态 = 重载 + 模板,编译确定,快,不灵活
- 运行时多态 = 继承 + 虚函数,运行确定,灵活,稍慢
- Qt 上位机、框架、插件化 → 大量使用运行时多态
最强记忆口诀
编译多态看重载, 运行多态看虚函数; 编译快但不灵活, 运行灵活框架用!