override用于显式声明覆盖虚函数,防止签名不匹配导致的静默新函数;final用于禁止继承或重写,二者均为编译期检查,零运行时开销。

override 用来告诉编译器“我明确想覆盖基类虚函数”
不加 override 时,哪怕函数签名稍有不同(比如参数类型不一致、const 修饰不匹配、返回类型协变没写对),编译器也不会报错,而是静默地声明了一个**新函数**,而不是覆盖。这会导致运行时调用不到预期的派生类版本,bug 难以发现。
加上 override 后,编译器会严格比对:该函数是否确实存在于某个可访问的基类中、是否为虚函数、签名是否完全匹配(含 const/volatile/ref-qualifier)。不满足任一条件就直接报错。
- 常见错误现象:
void func(int)在基类中是virtual void func(double),子类写void func(int) override→ 编译失败 - 使用场景:所有你本意是覆盖虚函数的地方,都应加
override,尤其是重构后或多人协作时 - 性能影响:零开销,纯编译期检查
final 用于禁止进一步继承或覆盖
final 可作用于两个位置:类定义后(禁止被继承)、虚函数声明后(禁止被重写)。它不是运行时控制,而是编译期强制约束。
- 类上加
final:class Widget final : public Base { ... };→ 其他类再继承Widget会触发编译错误:error: cannot derive from 'final' class - 函数上加
final:virtual void draw() final;→ 派生类中再写void draw() override会报错:error: virtual function 'draw' cannot be overridden - 注意:不能同时用
override和final修饰同一个函数——语法允许,但语义上合理(覆盖并封顶),例如:void draw() override final;
不写 override 的真实风险:签名漂移无人察觉
下面这段代码在没有 override 时能编译通过,但行为不符合直觉:
立即学习“C++免费学习笔记(深入)”;
struct Base {
virtual void log(const std::string& msg) {}
};
struct Derived : Base {
void log(std::string msg) { /* 注意:少了 const& */ }
};
Base* p = new Derived();
p->log("hello"); // 调用的是 Base::log,不是 Derived::log!
加了 override 后,Derived::log 那一行立刻报错,问题暴露得早且明确。
- 容易踩的坑:用 IDE 自动生成重写函数时,有些老版本插件不自动补
override - 兼容性:C++11 起支持,所有现代编译器(GCC 4.7+、Clang 3.2+、MSVC 2012+)都完整支持
override 和 final 不解决运行时多态逻辑错误
它们只管“有没有覆盖”和“能不能覆盖”,不管覆盖后的函数逻辑是否正确。比如:
- 子类
override了clone(),但内部返回了new Base而非new Derived→ 编译通过,运行时出错 - 把
final加在关键算法函数上防止误改,但若该函数本身有逻辑缺陷,final反而让修复更麻烦(需改设计)
真正容易被忽略的是:这两个关键字必须配合虚函数机制起作用;非虚函数加 override 是语法错误,加 final 则无意义。









