当前位置: 首页 > news >正文

C++类成员函数 重写、覆盖与隐藏

在 C++ 继承中,重写(Override)覆盖(Hide / 隐藏(Shadow) 是与函数继承相关的三个重要概念。尽管它们都涉及子类和父类中同名函数的行为,但它们的定义、用途和效果完全不同。本文档将详细介绍这三个概念的区别、代码示例以及最佳实践。


一、核心区别

术语英文名是否需要虚函数签名是否一致是否支持多态典型场景
重写Override实现多态,动态绑定
覆盖/隐藏Hide/Shadow否或签名不同子类定义同名函数,隐藏父类版本

二、详细说明与示例

1. 重写(Override)

定义:子类重新定义父类的虚函数(virtual),签名(函数名、参数列表、返回类型)必须完全一致,用于实现运行时多态(动态绑定)。

特点

  • 必须在父类中声明为 virtual
  • 子类函数可使用 override 关键字(C++11 引入,推荐使用)确保正确重写。
  • 通过基类指针或引用调用时,根据对象的实际类型执行子类或父类的实现。

代码示例

#include <iostream>class Base {
public:virtual void show() {std::cout << "Base show()" << std::endl;}
};class Derived : public Base {
public:void show() override {std::cout << "Derived show()" << std::endl;}
};int main() {Base* ptr = new Derived();ptr->show(); // 输出:Derived show()ptr->Base::show(); // 输出:Base show()delete ptr;return 0;
}

说明

  • ptr 指向 Derived 对象,调用 show() 时执行子类的实现,体现多态。
  • 使用 override 关键字可避免签名错误,编译器会检查是否正确重写。

2. 覆盖/隐藏(Hide/Shadow)

定义:子类定义了一个与父类同名但签名不同(或非虚函数)的函数,导致父类的同名函数被“隐藏”。这不是多态,而是静态行为。

特点

  • 不需要父类函数是虚函数。
  • 子类函数签名可以与父类不同(参数列表或返回类型不同)。
  • 通过子类对象调用时,只调用子类的版本;通过父类指针/引用调用时,调用父类的版本。
  • 隐藏会影响所有父类的同名函数版本(包括重载版本)。

代码示例 1:非虚函数的隐藏

#include <iostream>class Base {
public:void show() {std::cout << "Base show()" << std::endl;}
};class Derived : public Base {
public:void show() { // 隐藏父类的 show()std::cout << "Derived show()" << std::endl;}
};int main() {Derived d;d.show(); // 输出:Derived show()Base* ptr = &d;ptr->show(); // 输出:Base show()(无多态)return 0;
}

代码示例 2:签名不同导致隐藏

#include <iostream>class Base {
public:virtual void show(int x) {std::cout << "Base show(int): " << x << std::endl;}
};class Derived : public Base {
public:void show() { // 隐藏父类的 show(int)std::cout << "Derived show()" << std::endl;}
};int main() {Derived d;d.show(); // 输出:Derived show()// d.show(10); // 错误:Base::show(int) 被隐藏Base* ptr = &d;ptr->show(10); // 输出:Base show(int): 10return 0;
}

解决隐藏问题

  • 使用 using Base::show; 在子类中引入父类的函数,恢复被隐藏的重载版本。
class Derived : public Base {
public:using Base::show; // 恢复父类的 show(int)void show() {std::cout << "Derived show()" << std::endl;}
};或者
d.Base::show(10); // 输出:Base show()//无论是 重写(Override)、隐藏(Hide) 还是其他情况,都可以通过 Base:: 显式指定作用域 来调用父类的函数。这是 C++ 中访问被覆盖或隐藏的基类成员的一种直接方式。

三、总结与对比

特性重写 (Override)覆盖/隐藏 (Hide/Shadow)
虚函数要求必须是 virtual不需要
签名要求必须完全一致可以不同
多态性支持(动态绑定)不支持(静态绑定)
调用行为由对象实际类型决定由指针/引用类型决定
典型问题忘记 virtual 或签名不匹配意外隐藏父类函数
解决方法使用 override 检查使用 using 恢复父类函数

四、最佳实践

  1. 重写时使用 override 关键字
    • 确保函数正确重写父类的虚函数,编译器会检查签名是否匹配。
    • 提高代码可读性和可维护性。
  2. 避免意外隐藏
    • 定义子类函数时,检查是否与父类同名。
    • 如果需要父类函数版本,使用 using Base::func; 引入。
  3. 明确设计意图
    • 如果需要多态,始终在父类中使用 virtual
    • 如果不需要多态,避免在子类中使用与父类同名的函数,以免混淆。
  4. 测试多态行为
    • 使用基类指针或引用测试函数调用,确保行为符合预期。

五、常见问题与解答

Q1:为什么子类的同名函数会隐藏父类的所有同名重载版本?

  • C++ 的名称查找规则(Name Lookup)在子类中找到同名函数后停止搜索,导致父类的所有同名函数被隐藏。

Q2:如何判断是重写还是隐藏?

  • 检查父类函数是否为

    virtual
    

    且签名是否一致:

    • 是虚函数且签名一致 → 重写。
    • 不是虚函数或签名不同 → 隐藏。

Q3:隐藏是 bug 还是特性?

  • 隐藏是 C++ 的语言特性,但常导致意外行为,因此需要小心使用。

通过理解重写与隐藏的区别,开发者可以更好地设计继承体系,避免常见的错误并实现预期的多态行为。

相关文章:

  • 头歌java课程实验(函数式接口及lambda表达式)
  • 七段码 路径压缩 并查集 dfs
  • Datawhale AI春训营 TASK2 学习笔记
  • 用P0口控制LED(STC89C52单片机)
  • QEMU源码全解析 —— 块设备虚拟化(20)
  • 硬件电路(25)-过温保护器件ksd9700温控开关
  • 图例QCPLegend
  • 深入理解基线检查:网络安全的基石
  • 基于 JavaWeb 的 SpringBoot 办公 ERP 管理系统设计与实现(源码+文档+部署讲解)
  • 从浏览器地址栏输入 URL 到网页显示,这中间发生了什么?
  • [matlab]子图排版和线性回归
  • MySQL8启动失败 NET HELPMSG 3534
  • 016-C语言内存函数
  • 【HarmonyOS 5】VisionKit人脸活体检测详解
  • 【特殊场景应对3】创意岗简历骚操作:作品集链接的正确打开方式
  • 【Vue】组件通信(Props/Emit、EventBus、Provide/Inject)
  • keil5烧录后No Debug
  • (三)mac中Grafana监控Linux上的Redis(Redis_exporter安装使用)
  • 在win上安装Ubuntu安装Anaconda(linx环境)
  • 6.数据手册解读—运算放大器(三)
  • 独家丨远洋渔船船长被害案嫌犯移送检方报捕,船上两段视频曝光
  • 明查|俄罗斯征兵部门突袭澡堂抓捕壮丁?
  • 成了“一日顶流”又能如何?
  • 拒绝“假期刺客”,澎湃启动“五一”消费维权线索征集
  • 张小泉:控股股东所持18%股份将被司法拍卖,不会导致控制权变更
  • 奥利弗·沙赫特博士:集群是产业集聚地,更是“超级连接器”