虚函数重写必须满足签名完全一致:参数列表、const/volatile限定符、引用限定符(&/&&)须完全相同,返回类型需协变,基类函数必须为virtual,否则视为隐藏而非重写。

虚函数重写(override)必须满足签名完全一致
虚函数重写是运行时多态的基石,但它的触发条件非常严格:基类函数必须带 virtual,派生类中同名函数的参数列表、const 限定符、引用/指针修饰(如 & 或 &&)必须和基类一模一样,返回类型也需满足协变规则(比如基类返回 Base*,派生类可返回 Derived*)。哪怕只差一个 const,编译器就不会当作重写,而是当成隐藏。
常见错误现象:error: 'func' does not override any member functions(用了 override 关键字但不匹配)或静默变成隐藏而不报错。
- 基类没加
virtual→ 不可能重写,只能隐藏 - 派生类函数参数少了一个
const→ 编译器不认为是重写,调用时不会走虚表 - 返回类型不是协变(比如基类返回
int,派生类返回double)→ 重写失败
同名函数隐藏(hide)不关心参数或 virtual,只看名字
只要派生类定义了和基类同名的成员函数,不管参数是否相同、基类函数是否为虚函数,基类的整个名字在派生类作用域里就被屏蔽了。这意味着:即使基类有 void foo(int) 和 void foo(double) 两个重载版本,派生类只要定义一个 void foo(std::string),那两个基类版本全不可见——连 foo(42) 都会编译失败。
使用场景:你确实想彻底替换基类接口,且不希望用户还能通过派生类对象调用基类旧版函数。
立即学习“C++免费学习笔记(深入)”;
- 隐藏后想调用基类函数?必须显式写
obj.BaseClass::func(...) - 隐藏不依赖
virtual,所以和多态无关,绑定发生在编译期 - 隐藏发生时,基类所有同名重载函数全部失效,不只是签名相近的那个
为什么 override 失败后常常误以为是“重载”
很多人在派生类里写了参数略有不同的函数,以为是在“重载基类函数”,结果发现基类版本根本调用不了——这是因为 C++ 规定:重载只发生在同一作用域内;而基类和派生类是不同作用域,同名即隐藏,不存在跨作用域重载。
典型误判:B::func(double) 和 A::func(int) 看似像重载,实际是隐藏。派生类对象 b 调用 b.func(3.14) 成功,但 b.func(42) 直接报错,而不是退回到基类版本。
- 想保留基类重载?得在派生类里用
using Base::func;引入所有基类同名函数 - 没写
using又想调用基类某一个重载?只能靠作用域解析b.Base::func(42) - C++11 后强烈建议对重写函数加上
override,它能帮你提前捕获隐藏而非重写的错误
容易被忽略的关键点:访问控制和 using 声明的影响
隐藏行为不受 public/protected/private 影响——哪怕基类函数是 public,派生类定义了同名 private 函数,它依然隐藏基类版本。而 using 声明可以改变隐藏的边界,但它本身不构成重写,只是把基类名字重新注入当前作用域。
-
using Base::func;必须写在派生类定义内部,且位置影响可见性(通常放 public 区) -
using后,派生类自己的同名函数和基类引入的函数才真正构成“同作用域内的重载” - 虚函数一旦被隐藏,
using不能恢复其虚特性;要重写,还是得在派生类中定义签名完全匹配的virtual函数
override 和 using 不是可选项,而是防止意外屏蔽的必要手段。


















