override是C++11引入的编译期校验工具,强制检查派生类函数是否正确重写基类虚函数,要求参数类型、const/volatile限定符、引用类型、noexcept等严格匹配,仅返回类型可协变,能防止假重写和多态失效,零运行时开销。

override 关键字用来显式声明虚函数重写
它不是可有可无的修饰符,而是编译器校验工具:当你在派生类中加了 override,编译器会立刻检查基类是否存在匹配的虚函数。不匹配(比如参数类型不对、const 修饰不一致、返回类型协变失败)就直接报错,而不是静默忽略或意外调用基类版本。
常见错误现象:virtual void foo(int); 在基类中,派生类写成 void foo(double) override; → 编译失败,提示“no function to override”;没加 override 则可能被当成新函数,运行时调用不到,bug 难排查。
- 必须作用于成员函数声明(不能用于定义),且该函数必须是虚函数(继承自基类虚函数)
- 参数类型、const/volatile 限定符、引用/右值引用、noexcept(C++17 起)都必须严格匹配,否则编译不通过
- 返回类型允许协变(如基类返回
Base*,派生类可返回Derived*),但前提是满足继承关系
不加 override 可能导致“假重写”和多态失效
没有 override 时,编译器不会强制你重写虚函数。哪怕函数签名看着像,只要稍有差异(比如少了个 const),它就变成一个全新的重载函数,而非重写。此时通过基类指针调用,仍执行基类实现,行为与预期不符。
使用场景:维护大型继承体系时,基类接口调整(如增加 const 限定)后,若子类未同步更新,不加 override 就无法暴露问题。
立即学习“C++免费学习笔记(深入)”;
- 示例:
virtual void draw() const;基类;子类写void draw();(无 const)→ 不重写,也不报错;加上override后立即编译失败 - 性能影响为零:
override是纯编译期标记,不生成额外代码,不影响运行时开销 - 兼容性:C++11 起支持,老项目升级时建议逐步补全,尤其在重构虚函数接口后
override 和 final 的组合使用能防误继承和误重写
final 用在虚函数声明末尾,表示该函数不可再被派生类重写;用在类名后,表示该类不可被继承。和 override 连用,能形成清晰的“此处已重写,且到此为止”的契约。
容易踩的坑:把 final 加在非虚函数上会报错;在函数声明中 override 和 final 顺序可互换,但习惯上写成 virtual void f() override final; 或 void f() override final;。
-
void func() override final;表示:我重写了基类虚函数,且后续任何子类都不许再改它 - 若某类已用
final修饰,其所有虚函数自动具备“不可重写”语义,再加override final属于冗余但合法 - 滥用
final会降低扩展性,适合明确设计为“叶子节点”的类(如具体策略实现)
现代 C++ 项目应默认启用 override,IDE 和 linter 可辅助补全
它本质是编译器提供的“防呆”机制,成本极低,收益明确。很多团队已将其纳入编码规范,CI 流程中配合 clang-tidy 的 modernize-use-override 检查项自动提醒遗漏。
注意点:模板类中的虚函数重写也适用 override,但需确保实例化时基类虚函数确实存在;对于依赖 SFINAE 或特化的场景,要小心编译器是否能准确推导出重写关系。
- Clang/GCC/MSVC 全支持,无需特殊开关
- VS IntelliSense、CLion、vscode + C/C++ extension 均能识别并高亮错误重写
- 真正容易被忽略的是 const 成员函数、noexcept 规范、以及右值引用形参这些“看起来一样其实不同”的细节










