虚函数通过对象的vptr和类的vtable实现动态绑定,需满足virtual声明、派生类重写、指针或引用调用三条件;构造/析构中虚函数不“虚”;有轻微性能开销但通常可忽略。

虚函数靠虚表指针和虚表实现动态绑定
虚函数的运行时多态不是编译器“猜”调用哪个函数,而是靠对象内存布局里隐含的 vptr(虚表指针)和类对应的 vtable(虚函数表)协作完成。每个含虚函数的类在编译期生成一张函数指针数组,对象构造时自动把 vptr 指向对应类的 vtable;调用虚函数时,实际执行的是 (*obj->vptr[n])(obj) 这样的间接跳转。
必须满足三个条件才能触发虚调用
缺一不可,否则看起来像多态,实际是静态绑定:
- 基类函数声明为
virtual(纯虚也行) - 派生类中用相同签名重写该函数(
override关键字强烈建议加上,避免拼错或参数类型不匹配) - 必须通过指针或引用调用——直接用对象变量调用(如
Base b; b.func();)永远走静态绑定
常见踩坑:构造/析构期间虚函数不“虚”
在构造函数和析构函数里调用虚函数,不会调用派生类重写的版本,只调用当前正在构造/销毁的那个类的版本。这是因为 vptr 在构造过程中是逐步调整的:进入 Derived 构造函数前,vptr 先指向 Base::vtable,等 Base 部分构造完才更新为 Derived::vtable。所以:
- 别在构造函数里调用虚函数,尤其别依赖派生类的初始化逻辑
- 析构同理,
~Derived()执行完后vptr就被改回Base::vtable,再调虚函数就崩了 - 如果真需要类似行为,显式调用
Derived::func()或改用工厂+初始化函数模式
虚函数有开销,但现代 CPU 下通常可忽略
每次虚调用多一次指针解引用(查 vtable)+ 一次间接跳转,比普通函数调用多 1–2 个周期。影响主要在极端场景:
立即学习“C++免费学习笔记(深入)”;
- 高频小函数(如向量加法内联失败)——考虑是否真需要虚,或用模板替代
- 嵌入式资源受限环境——确认 ABI 是否启用 RTTI 和虚表(有些裁剪版 STL 会关掉)
- 注意虚表本身不共享:即使两个派生类重写同一虚函数,它们的
vtable里仍各自存一份函数指针,但目标地址相同
std::unique_ptr<base> 当成“安全多态容器”却忘了它内部仍是裸指针语义——只要底层对象析构了,虚调用就变成野指针跳转。


















