override用于显式声明重写虚函数,强制编译器检查签名匹配;final用于禁止继承或进一步重写,二者均为C++11引入的编译期约束关键字。

override 用来明确表示重写虚函数
不加 override 时,编译器不会检查你是否真的重写了父类虚函数。比如参数类型写错、const 修饰不一致、返回类型协变没处理好,都会悄悄变成新函数(重载),而不是重写——结果就是多态调用不生效,bug 很难发现。
加上 override 后,编译器强制校验:必须存在可访问的、签名匹配的虚函数在基类中,否则直接报错,错误信息通常是 error: 'func' does not override any member functions。
- 只能用于成员函数声明(定义里不能写)
- 必须和基类虚函数的 const/volatile/ref-qualifier 完全一致
- 返回类型需满足协变规则(如基类返回
Base*,派生类可返回Derived*) - 不能用于非虚函数,也不能用于构造/析构/重载运算符(除非它们本身是虚的)
示例:
struct Base {
virtual void foo(int) const;
virtual ~Base() = default;
};
struct Derived : Base {
void foo(int) const override; // ✅ 正确
void foo(double) const override; // ❌ 编译失败:基类没有这个签名
void bar() const override; // ❌ 编译失败:Base 没有虚函数 bar
};final 阻止进一步继承或重写
final 是个“终止符”,有两个作用位置:类名后阻止继承,函数声明后阻止重写。它不改变语义,只加编译期限制,一旦违反就报错,比如 error: cannot derive from 'final' base class 或 error: function 'f' marked 'final' cannot be overridden。
立即学习“C++免费学习笔记(深入)”;
- 写在类定义末尾:
struct X final { ... };→ 其他类不能class Y : X {} - 写在虚函数声明末尾:
virtual void f() final;→ 派生类不能再override它 -
final和override可以一起用:void f() override final;,表示“我重写了,且到此为止” - 不能用在非虚函数、非成员函数、模板声明上
典型场景:想封装一个策略类,允许用户继承一次实现定制逻辑,但禁止继续派生以防误用;或某个关键虚函数逻辑已固化,不允许下游修改。
override 和 final 的组合用法很常见
实际项目中,很多基类接口设计成“开放—封闭”风格:顶层定义虚函数,中间层实现并封住,避免下游随意改动。这时 override final 就很有用。
- 不是所有虚函数都需要 final,只有那些“稳定不可变”的才加
- 过度使用
final会降低扩展性,比如单元测试时想 mock 就可能被卡住 -
override应该作为日常习惯加在每个重写函数上,哪怕基类当前只有一个虚函数——因为未来基类可能新增,而你忘了同步更新派生类 - 注意:
final对虚析构函数也有效,但通常不加,因为派生类需要能定义自己的析构逻辑
示例:
struct Strategy {
virtual void execute() = 0;
virtual ~Strategy() = default;
};
struct FastStrategy : Strategy {
void execute() override final { /* 固化实现 */ }
};容易忽略的细节和兼容性问题
这两个关键字是 C++11 引入的,但部分老项目还在用 C++98/03 标准,或交叉编译环境(如某些嵌入式工具链)可能不完全支持。确认方式是看编译器是否接受 -std=c++11 或更高标准,并检查预处理器宏:__cplusplus >= 201103L。
- Clang/GCC/MSVC 主流版本都支持,但 MSVC 2013 以前对
final的语法支持不完整(比如类后 final 有时被忽略) - 不要把
final写在函数定义(.cpp 文件里),只能出现在头文件的声明中 - 宏展开后意外插入
override或final可能导致语法错误,建议避免在宏里拼接这些关键字 - 静态分析工具(如 clang-tidy)常带
modernize-use-override检查项,可以批量补全遗漏的override
真正麻烦的不是语法怎么写,而是团队是否统一认知:哪些函数该标记、谁有权改 final、要不要在抽象基类里提前预留 override 点。这些规范一旦松动,关键字就只剩形式意义了。










