override必须严格匹配虚函数签名(参数、const、noexcept、返回类型协变),仅用于类内声明末尾,可捕获隐藏错误并支持模板,是编译期契约校验机制。

override 关键字必须和虚函数声明严格匹配
加 override 不是“只要父类有同名函数就能用”,它会强制检查:子类函数签名(包括参数类型、const 修饰、noexcept、返回类型协变)是否**精确匹配**基类的虚函数。哪怕漏一个 const,编译器就报错:error: 'func' does not override any member functions。
常见踩坑点:
- 父类函数是
virtual void draw() const;,子类写成void draw() override;→ 少了const,不匹配 - 父类返回
Shape*,子类返回Circle*(协变允许),但若父类是const Shape*,子类返回Circle*就不协变 → 编译失败 - 父类声明了
noexcept,子类没写或写了noexcept(false)→ 不匹配
override 只能用于成员函数,且不能出现在函数定义处
override 是说明符(specifier),不是修饰符,它只出现在**类内函数声明末尾**,绝不能出现在函数体外的定义中(比如 .cpp 文件里的实现)。否则编译器直接报错:error: 'override' cannot be used outside a class definition。
正确写法示例:
立即学习“C++免费学习笔记(深入)”;
class Base {
public:
virtual void step(int x) = 0;
};
class Derived : public Base {
public:
void step(int x) override { /* 实现 */ } // ✅ 声明处加 override
};错误写法:
-
void Derived::step(int x) override { ... }(在 .cpp 中定义时加)→ 报错 -
virtual void step(int x) override(同时写virtual和override)→ 允许但冗余,override已隐含其为虚函数
没加 override 也可能“看似重写成功”,但实际是隐藏(hiding)
当子类函数签名和父类虚函数**不完全一致**(比如参数类型不同、少 const、多默认参数),又没加 override,编译器不会报错,而是静默地把子类函数当作新函数——即**隐藏基类函数**。调用时若通过基类指针/引用,仍会调用父类版本,逻辑出错却无提示。
例如:
Base* p = new Derived(); p->step(5); // 看似该调子类,但如果 signature 不匹配,实际调 Base::step
加 override 后这类问题会在编译期暴露,避免运行时行为诡异。
override 对模板类中的虚函数同样生效,但需注意实例化时机
模板基类里的虚函数,在派生类中重写时也可以用 override,但要注意:模板未实例化前,编译器可能无法完成匹配检查。也就是说,如果基类模板没被具体化(如没出现 Base<int></int> 这种用法),有些编译器(尤其旧版 GCC)可能跳过 override 校验。
稳妥做法:
- 确保基类模板已被实例化(至少有一次显式使用)
- 用现代编译器(GCC 7+、Clang 5+、MSVC 2017+),它们对模板中
override的支持更严格 - 避免在模板参数未定的上下文中依赖
override做安全兜底
虚函数重写的真正安全边界不在语法糖,而在签名一致性;override 是编译器给你的校验钩子——不用它,等于主动放弃一层最廉价的契约保障。











