不加 override 会导致“看似重写,实则新增”,多态在运行时彻底失效,而编译器一声不吭;因为 c++03 及更早标准中,只要函数名相同、参数列表不同,就自动视为重载,编译器不检查是否意图重写,仅按签名匹配决定是重写还是新函数。

不加 override 会导致“看似重写,实则新增”,多态在运行时彻底失效,而编译器一声不吭。
为什么编译器不报错,但行为完全不对?
因为 C++03 及更早标准中,只要函数名相同、参数列表不同,就自动视为重载;哪怕你本意是重写,只要签名差一丁点(比如漏了 const、&、参数类型从 int 写成 int&),编译器就当它是新函数——父类虚函数还在,子类函数只是个普通成员函数。调用时走静态绑定,取决于指针/引用的静态类型,而不是实际对象类型。
常见错误现象:
-
Base::func() const存在,子类写func()(没const)→ 编译通过,但调用ptr->func()仍执行基类版本 - 父类是
virtual void draw(int x),子类写void draw(float x) override→ 直接编译失败,提前暴露问题 - 拼错函数名:
dispay()而非display()→ 不加override时静默编译成功,加了立刻报error: 'dispay' does not override any member functions
override 的实际检查项有哪些?
它不是装饰,是编译期契约。加上后,编译器会逐项核对:
- 基类中是否存在同名、可访问的
virtual函数(含纯虚) - 参数类型、个数、顺序是否完全一致(顶层
const可忽略,但intvsconst int&不行) -
const/volatile限定符必须一致(void f() const不能被void f()重写) - 引用限定符必须一致(
void f() &和void f() &&视为不同函数) - 返回类型需协变:仅限指针/引用,且派生类返回类型必须是基类返回类型的派生类(
Base*→Derived*合法;Base→Derived值类型非法) -
noexcept说明符(C++17 起)也需匹配
哪些地方容易漏掉 override?
最常被忽略的不是语法错误,而是设计惯性:
- 重构基类虚函数后(比如加了个默认参数、改了
const),忘记同步更新所有子类的override声明 → 编译失败反而是好事,否则多态悄悄退化 - 在多重继承中,两个基类都有同名虚函数(如都定义了
virtual void init()),子类实现时加override可能触发二义性警告,这时得用作用域解析明确指定:Base1::init() override或Base2::init() override - 析构函数:基类有
virtual ~Base(),子类析构函数虽隐式为虚,但仍建议显式写~Derived() override—— 防止未来基类析构函数被意外改成非虚 - 模板派生类中重写虚函数:
template<typename t> class Derived : public Base { void func() const override; };</typename>——override必须放在声明末尾,不能放在定义里
真正麻烦的不是写错,是“没报错却错了”。override 不改变运行时行为,只把潜在的逻辑断裂点提前到编译阶段。一旦项目规模上来,靠人工核对虚函数签名几乎不可行,而漏掉一个 const 就可能让整个策略模式失效。它不是可选项,是现代 C++ 多态的基础设施。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











