虚函数是实现运行时多态的必要条件,必须通过指针或引用调用才生效;虚析构函数是资源安全底线;override 是编译期契约,确保正确重写;纯虚函数强制抽象类不可实例化且派生类必须实现。

虚函数是 C++ 实现运行时多态的必要条件,不是可选技巧——没它,基类指针调用派生类行为就只能靠 if-else 硬判断,代码会迅速失控。
虚函数必须通过指针或引用调用才生效
直接用对象调用,哪怕函数声明为 virtual,也走静态绑定。比如:
Base b; Derived d; b.func(); // 调用 Base::func(),跟 virtual 无关 d.func(); // 调用 Derived::func(),但这是静态调用,不依赖虚机制 Base* ptr = &d; ptr->func(); // ✅ 才触发虚函数机制,实际调用 Derived::func()
常见错误是误以为“只要写了 virtual 就自动多态”,结果在栈上直接构造对象、直接调用,完全没触发动态绑定。
虚析构函数不是“建议”,而是资源安全底线
如果基类有虚函数,但析构函数不是虚的,用 Base* 删除派生类对象会导致未定义行为(派生类部分内存不释放):
- 基类没写
virtual ~Base()→delete ptr;只调Base::~Base(),Derived的析构逻辑被跳过 - 即使基类目前没资源要清理,只要未来可能加成员(比如文件句柄、堆内存),就必须提前设虚析构
- 编译器不会报错,但 ASan/UBSan 能捕获这类内存泄漏或崩溃
override 关键字不是装饰,是编译期契约
写 void func() override 表示你明确断言:这个函数必须重写基类中某个虚函数。编译器会检查三件事:
- 基类是否存在同签名的虚函数
- 参数类型、const 限定、引用限定是否完全一致
- 返回类型是否满足协变规则(如基类返回
Base*,派生类可返回Derived*)
漏写 override 很容易因拼写错误(比如 draw() 写成 drow())导致“看似重写实则隐藏”,调用时静默走基类实现,调试极难发现。
纯虚函数让抽象意图不可绕过
virtual void draw() = 0; 不只是“没实现”,它强制两点:
- 该类变成抽象类,无法实例化:
Shape s;编译失败 - 所有非抽象派生类必须提供定义,否则仍是抽象类
这比文档或注释更可靠。比如图形系统里,Shape 本就不该有“通用绘制”逻辑,硬写个空实现反而误导使用者去调它。
虚函数表(vtable)和虚指针(vptr)是编译器自动管理的底层机制,你不需要手动操作,但得清楚:每个含虚函数的类对象都多一个指针开销(通常 8 字节),且虚函数调用比普通函数多一次间接寻址——性能敏感路径(如 inner loop)要权衡是否值得。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











