C++的多态是如何体现的?
一篇尽量清晰、结构化的文章,帮你搞懂虚函数机制、vtable、虚表指针,以及最容易出错的那些点。
1. 多态在 C++ 里到底是什么?
C++支持三种多态:
- 编译时多态(静态多态):函数重载、运算符重载、模板(泛型)、CRTP
- 运行时多态(动态多态):通过虚函数 + 指针/引用实现
- 强制多态(类型转换):static_cast、dynamic_cast 等(较少讨论)
绝大多数人问'C++的多态'时,指的其实就是运行时多态,也就是通过虚函数实现的动态绑定。
一句话总结核心:
同一个接口,不同的对象表现出不同的行为,且绑定发生在运行时。
2. 虚函数机制的核心——虚表(vtable)与虚表指针(vptr)
C++的运行时多态实现依赖于以下几个关键概念:
| 概念 | 英文 | 存放在哪里 | 内容是什么 | 谁拥有它 |
|---|---|---|---|---|
| 虚函数表 | virtual table (vtable) | 静态存储区(每个类一份) | 该类的所有虚函数的地址(函数指针数组) | 类(类型) |
| 虚表指针 | virtual pointer (vptr) | 对象内存布局最开头(通常) | 指向本对象对应类的 vtable 的指针 | 每个对象 |
| 虚函数调用 | dynamic dispatch | — | 通过 vptr 找到 vtable,再通过槽位找到函数地址 | 运行时 |
最重要的一句话:
只要一个类有虚函数(或继承自有虚函数的类),编译器就会为这个类生成一张虚表,并在类的对象中偷偷插入一个虚表指针 vptr。
3. 虚函数调用流程(最关键的图解过程)
假设有下面这段经典代码:
class Animal {
public:
virtual void speak() {
std::cout << "Animal speaks\n";
}
virtual ~Animal() = default;
};
class Dog : public Animal {
public:
void {
std::cout << ;
}
};
: Animal {
:
{
std::cout << ;
}
};
{
Animal* p = ();
p->();
p;
}

