基类普通函数中调用虚函数仍为动态绑定,因编译器依据运行时this的动态类型查vtable;仅构造/析构函数中因对象未完全构建或已部分销毁而退化为静态绑定。

会执行当前对象实际类型的版本,不是基类内部写死的版本。
基类普通成员函数里调用虚函数,为什么不是“静态绑定”?
很多人误以为“普通函数里调用虚函数”就等于编译期决定调用哪个版本,其实只要调用表达式满足「通过 this(隐式或显式)调用虚函数」,且对象是多态类型(即有虚函数、通过指针/引用传入或本身是派生类对象),C++ 就仍走动态绑定流程。
关键不在“谁写的调用”,而在“调用时的 this 指向什么类型”。编译器看的是运行时 this 的动态类型,不是当前函数定义在哪个类里。
- 基类普通函数中写
speak(),等价于this->speak() - 如果这个函数被派生类对象调用(比如
dog.speakFromBase()),this就是指向Dog对象的Dog*,哪怕它在Animal::speakFromBase里 - 所以最终调的是
Dog::speak(),不是Animal::speak()
常见错误现象:构造函数里调用虚函数不生效
这是唯一一个「明明写了虚函数,却没走动态绑定」的典型场景。原因在于:构造顺序是从基类到派生类,当执行基类构造函数时,派生类部分尚未初始化,this 的动态类型就是基类本身。
- 在
Animal构造函数里调用speak()→ 只能调Animal::speak() - 即使对象最终是
Dog类型,此时this的动态类型仍是Animal - 同理,析构函数中调用虚函数也只绑定到当前正在析构的类版本
override 和 final 对这种调用有影响吗?
没有直接影响。它们只约束派生类能否重写、是否允许进一步派生,不改变虚调用的绑定逻辑。
-
override是编译期检查:确保你写的函数确实在基类中有对应虚函数,防止拼错名或参数不匹配 -
final阻止后续派生类重写该函数,但不影响当前层级的动态绑定行为 - 真正起作用的,始终是
virtual声明 + 运行时this的动态类型
最容易被忽略的是构造/析构阶段的限制——那里虚函数“退化”成普通函数。其他所有常规调用路径,哪怕嵌套再深,只要没脱离多态对象上下文,就一定走 vtable 查找。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











