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

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++ 的语言特性,但常导致意外行为,因此需要小心使用。

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

http://www.xdnf.cn/news/789.html

相关文章:

  • 本地离线安装Ollama
  • 学习笔记:黑马程序员JavaWeb开发教程(2025.3.24)
  • 数据库知识
  • MySQL -数据类型
  • ZYNQ笔记(十):XADC (PS XDAC 接口)
  • 【项目实训个人博客】数据集搜集
  • 成品检验工程师心得总结
  • 基于ESP32 - S3实现一个ping百度的C测试程序
  • linux 搭建 dvwa 渗透测试环境
  • 6.数据手册解读—运算放大器(三)
  • AI日报 - 2025年04月20日
  • LangChain 单智能体模式示例【纯代码】
  • Spring Boot 集成 Spring Cloud 的详细教程
  • 学习笔记—C++—string(练习题)
  • 基于 LWE 的格密码python实战
  • STM32 HAL库Freertos 信号量的使用
  • c++类与对象(一)
  • Postgresql几个常用的json操作
  • dubbo SPI插件扩展点使用
  • [RHEL8] 指定rpm软件包的更高版本模块流
  • 深度解析微前端架构设计:从monorepo工程化设计到最佳实践
  • day 22 作业
  • python 字符串解析 struct.unpack_from(fmt, buffer, offset=0) ‘<? B I‘
  • Datawhale 春训营 创新药赛道
  • 011数论——算法备赛
  • 解决IDEA创建SpringBoot项目没有Java版本8
  • 线性回归之归一化(normalization)
  • SAP IAS云产品简介
  • AI与思维模型【70】——遗忘曲线
  • 金融图QCPFinancial