转向现代C++——在意为改写的函数添加 override

文章目录

    • [在意为改写的函数添加 override](#在意为改写的函数添加 override)
      • [未添加 `override`](#未添加 override)
      • [显式添加 `override`](#显式添加 override)
      • 引用限定符

在意为改写的函数添加 override

虚函数改写(Overriding)是实现多态的核心,但它的隐式匹配规则非常严苛。如果开发者稍有疏忽,本意是"改写(Override) "的函数就会变成"重载(Overload) "或"隐藏(Hide)",而编译器对此往往保持沉默。
虚函数改写的严格条件

在 C++ 中,派生类函数要想真正改写基类的虚函数,必须满足以下所有条件:

  1. 函数名相同
  2. 形参类型完全相同
  3. 常量性( const属性)完全相同
  4. 返回值类型和异常规格(Exception specification)必须兼容
  5. (C++11 新增) 引用限定符(Reference qualifiers)完全相同

未添加 override

cpp 复制代码
class Base {
public:
    virtual void doWork();          // 基础虚函数
    virtual void process(int x);    // 带参数
    virtual void display() const;   // const 函数
};

class Derived : public Base {
public:
    // 错误 1:漏掉了 const,变成了一个全新的虚函数,隐藏了 Base::doWork
    virtual void doWork(); 
    
    // 错误 2:参数类型写错(int 变成了 unsigned int),变成了重载(Overload)
    virtual void process(unsigned int x); 
    
    // 错误 3:函数名大小写写错,变成了完全无关的新函数
    virtual void DisPlay() const; 
};

:::color4

  • 编译期无警告****: 上述 Derived 中的三个函数,在 C++98/C++11 语法下完全合法。编译器会认为你是故意在派生类里增加新函数的。
  • 运行时行为诡异(多态失效)

:::

cpp 复制代码
Base* bp = new Derived();
bp->process(10); // 期望调用 Derived::process,但实际上调用了 Base::process!

显式添加 override

通过在派生类函数后面加上 override 关键字,向编译器明确传达了意图:"我这个函数绝对是改写基类的,请帮我检查!"

cpp 复制代码
class DerivedCorrected : public Base {
public:
    // 编译期报错!Base 中没有非 const 的 doWork()
    void doWork() override; 
    
    // 编译期报错!Base 中没有接收 unsigned int 的 process
    void process(unsigned int x) override; 
    
    // 编译期报错!Base 中没有名为 DisPlay 的函数
    void DisPlay() const override; 

    // ---------------- 正确的改写 ----------------
    void display() const override; // 完美匹配!编译通过
};

为什么强制使用 override?

  1. 让编译器做打字工 只要有任何一处不匹配(比如漏了 const,改了参数类型),编译器会立刻拒绝编译 ,并给出精准的错误提示。
  2. 无成本的代码文档 任何阅读代码的人一眼就能看出哪些函数是多态的关键节点,无需频繁跳回基类去确认。
  3. 安全重构基类 如果哪天你需要修改基类 Base::doWork() 的签名(比如加个参数),所有未同步修改的派生类都会在编译期报警,而不会在运行时悄悄崩溃。

引用限定符

C++11 允许根据对象是左值还是右值来调用不同的成员函数。如果基类指定了引用限定符,派生类改写时也必须完全一致,否则也会变成全新的函数

cpp 复制代码
class Widget {
public:
    // 只有左值对象 (*this 是左值) 才能调用此函数
    virtual void data() &;    
    
    // 只有右值对象 (*this 是右值) 才能调用此函数
    virtual void data() &&;   
};

class SubWidget : public Widget {
public:
    // 如果不加 override,这只是一个没有引用限定的新函数,会隐藏 Widget::data
    // 加上 override 后,编译器会立刻发现你漏掉了 & 或 && 从而报错
    void data() override;    // 错误:无法通过编译
    
    void data() & override;  // 正确:成功改写左值版本
};
相关推荐
繁星蓝雨37 分钟前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J1 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪
蒸蒸yyyyzwd1 小时前
机试学习笔记
c++·求职招聘
杜大哥1 小时前
python程序:如何查看电脑【电池电量的剩余百分比】 和 【是否插入连接着充电器】?
开发语言·python
wuyk5551 小时前
Python网络爬虫入门到实战 第01章:爬虫到底是什么?原理、流程、合法性、风险全解析(零基础必看)
开发语言·爬虫·python
yurenpai(27届找实习中)1 小时前
Java 10 的 var 为什么不能随便用?从订单汇总看懂类型推断与泛型陷阱
java·开发语言·java18
shmily麻瓜小菜鸡2 小时前
JS/TS 易踩坑知识点 — 模块化与工程化类
开发语言·javascript·ecmascript
geovindu2 小时前
CSharp: Observer Pattern
开发语言·后端·观察者模式·设计模式·c#·.netcore·行为模式
handler012 小时前
【Linux】线程与虚拟内存的底层世界
linux·运维·服务器·c++·线程·虚拟地址空间·共享内存
郝学胜-神的一滴3 小时前
C++11 工程级应用 09:告别无谓拷贝,解锁高性能移动语义
开发语言·数据结构·c++·vscode·软件工程·visual studio