虚函数调用取决于对象实际类型而非指针类型:运行时通过虚表动态绑定,只要指针指向派生类对象,即使使用基类指针也会调用派生类重写版本。

虚函数调用取决于对象实际类型,不是指针类型
结论很直接:C++ 虚函数调用走的是动态绑定(dynamic dispatch),运行时根据 对象实际类型 查虚表(vtable)决定调哪个函数,和指针/引用的静态类型无关。只要指针指向的是派生类对象,哪怕用基类指针访问,也会调派生类的重写版本。
为什么容易误以为看指针类型?
常见混淆点来自非虚函数、或指针本身不指向多态对象:
-
delete一个基类指针时,如果析构函数没声明为virtual,只会调基类析构——这不是虚函数机制的问题,而是析构未虚化导致未触发多态 - 用基类指针调用非虚成员函数,确实只看指针类型,因为不走虚表
- 指针为空(
nullptr)或指向已销毁对象,调虚函数会崩溃,此时“类型”已无意义 - 对象是栈上基类实例(如
Base b;),即使用Base*指向它,也永远调不到派生类函数——根本没派生类对象
验证虚调用行为的最小可测代码
struct Base {
virtual void f() { std::cout Base* ptr = new Derived;
ptr->f(); // 输出 "Derived::f",不是 "Base::f"
注意:override 不影响运行时行为,但能防止拼写错误或签名不匹配导致意外变成非虚调用;没有 override 或 virtual 修饰的 f() 在 Derived 中不会参与虚调度。
虚函数调用失效的几个典型场景
虚机制不是万能的,以下情况会导致“看似应该多态,实则没调对”:
- 构造函数/析构函数中调虚函数:此时虚表指针尚未完全初始化或已被销毁,调用的是当前正在构造/析构的那个类的版本
- 函数参数类型写死为基类值传递(
void foo(Base b)),发生对象切片(slicing),传进去的只剩基类部分,虚函数信息丢失 - 使用
static_cast强转指针后调用:若 cast 成了错误类型(比如把Derived*强转成无关的Other*),虚调用仍按实际对象类型走,但后续行为不可控 - 虚函数被内联(
inline)且编译器在编译期就确定了目标——这种情况极少,仅限于明确知道对象类型的局部场景(如栈对象直接调用),不影响多态语义
最常被忽略的是构造/析构期间的虚函数行为,以及切片问题——它们不违反“看实际类型”的原则,但让实际类型悄悄变了。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











