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

详细全面讲解C++中重载、隐藏、覆盖的区别

文章目录

      • 总结
      • 1、重载
        • 示例代码
        • 特点
        • 1. 模板函数和非模板函数重载
        • 2. 重载示例与调用规则
        • 示例代码
        • 调用规则解释
        • 3. 特殊情况与注意事项
            • 二义性问题
          • 函数特化与重载的交互
      • 2. 函数隐藏(Function Hiding)
        • 概念
        • 示例代码
        • 特点
      • 3. 函数覆盖(重写,Function Overriding)
        • 概念
        • 示例代码
        • 特点

总结

1、重载:在同一个作用域,函数名相同,参数列表不同,与返回值无关
2、隐藏:在基类和派生类之间发生的关系,函数名相同,派生类的函数把基类的函数给隐藏了,只关注函数数名,不管返回值和参数
3、覆盖:覆盖是隐藏的一种特殊情况,派生类和基类的函数,1 函数名相同;2 返回值相同;3 参数列表相同(不包括this指针在内);4 基类函数为虚函数;即为覆盖。

1、重载

  • 函数重载是指在同一个作用域(通常是在同一个类中,也可以是在全局作用域下)内,存在多个同名函数,但它们的参数列表(参数的个数、类型或者顺序)不同。编译器会根据调用时传递的实际参数来决定调用哪个具体的重载函数,函数重载实现了用同一个函数名来执行相似但参数有所不同的操作,提高了代码的可读性和易用性。
    总结来说就是:在同一个作用域,函数名相同,参数列表不同,与返回值无关
示例代码
#include <iostream>// 重载函数,参数个数不同
int add(int num1, int num2) {return num1 + num2;
}int add(int num1, int num2, int num3) {return num1 + num2 + num3;
}// 重载函数,参数类型不同
double add(double num1, double num2) {return num1 + num2;
}int main() {std::cout << add(2, 3) << std::endl;std::cout << add(2, 3, 4) << std::endl;std::cout << add(2.5, 3.5) << std::endl;return 0;
}

在上述代码中,add 函数有多个重载形式,有的重载函数参数个数不同(如两个 int 参数和三个 int 参数的版本),有的重载函数参数类型不同(如 int 参数和 double 参数的版本)。在 main 函数中调用 add 函数时,编译器根据传入的实际参数情况来选择匹配的重载函数进行调用。

特点
  • 作用域相同:重载的函数必须在同一个作用域内定义,比如都在某个类的内部或者都在全局作用域中。
  • 函数名相同:这是重载的关键特征之一,多个函数共享同一个函数名,方便代码的调用和理解,从使用者角度看好像是同一个函数根据不同情况执行不同逻辑。
  • 参数列表有差异:参数个数、类型或者顺序至少有一项不同,而返回类型不同不能作为函数重载的依据(因为仅返回类型不同时,编译器无法仅根据调用情况准确判断该调用哪个函数)。

除了以上常规的重载,还有一些同学认为泛型编程也存在重载,即模板函数和非模板函数的重载,
在C++中,模板函数与非模板函数之间可以存在重载关系,以下是关于它们重载的详细介绍:

1. 模板函数和非模板函数重载
  • 重载机制:重载允许在同一作用域内存在多个同名函数(对于函数重载而言,这些函数的参数列表有所不同,而返回类型不能作为区分重载的唯一依据),编译器会根据调用时实际传入的参数情况来决定调用哪一个具体的函数。模板函数和非模板函数的重载就是利用了这一机制,在合适的场景下,编译器会依据调用参数去选择调用模板函数版本还是非模板函数版本。
2. 重载示例与调用规则
示例代码
#include <iostream>// 非模板函数
int add(int num1, int num2){return num1 + num2;
}// 模板函数
template<typename T>
T add(T num1, T num2){return num1 + num2;
}int main(){int result1 = add(3, 5);  // 调用非模板函数 add(int, int)double result2 = add(3.5, 2.5);  // 调用模板函数 add<double>(double, double)std::cout << "整数相加结果: " << result1 << std::endl;std::cout << "浮点数相加结果: " << result2 << std::endl;return 0;
}
调用规则解释
  • 精确匹配优先:当进行函数调用时,编译器首先会寻找参数类型与函数参数列表能精确匹配的非模板函数。在上述代码的 add(3, 5) 调用中,非模板函数 add(int, int) 的参数类型刚好和传入的两个整数参数完全匹配,所以编译器优先选择调用这个非模板函数,而不会去考虑模板函数版本,即使模板函数也能够通过实例化(将 T 实例化为 int)来处理这两个整数参数。
  • 模板函数实例化匹配:如果没有找到精确匹配的非模板函数,编译器会尝试对模板函数进行实例化,看能否通过实例化后的模板函数来匹配调用参数。例如在 add(3.5, 2.5) 调用中,不存在参数类型为两个 double 的非模板函数 add,此时编译器会查看模板函数,将模板参数 T 实例化为 double,生成 add(double, double) 这样一个实例化后的函数版本,它能很好地匹配传入的两个 double 类型参数,所以就调用这个实例化后的模板函数版本。
3. 特殊情况与注意事项
二义性问题
  • 当模板函数和非模板函数的参数匹配存在模糊情况时,可能会导致编译错误,出现二义性问题。例如:
#include <iostream>// 非模板函数
void func(int num){std::cout << "非模板函数func(int)" << std::endl;
}// 模板函数
template<typename T>
void func(T num) 
{std::cout << "模板函数func(T)" << std::endl;
}int main() 
{func(5);  // 编译错误,存在二义性,不知道该调用模板函数还是非模板函数return 0;
}

在这个例子中,调用 func(5) 时,传入的整数 5 既可以匹配非模板函数 func(int),也可以通过将模板函数的 T 实例化为 int 来匹配模板函数 func(T),编译器无法确定到底该调用哪一个函数,就会报二义性的编译错误。要解决这类问题,可以通过显式指定模板参数(如 func<int>(5) 就会明确调用模板函数版本)或者调整函数的参数类型等方式,使得调用具有明确的匹配对象。这个问题并不是所有的编译器都存在,在VS2022就不存在该问题。

函数特化与重载的交互
  • 函数模板可以进行特化,即针对特定的类型提供专门的模板函数实现。在存在函数特化的情况下,特化版本、模板函数的通用版本以及非模板函数之间的重载关系也需要遵循上述的调用规则。例如:
#include <iostream>// 非模板函数
void printData(int num){std::cout << "非模板函数打印整数: " << num << std::endl;
}// 模板函数
template<typename T>
void printData(T data) 
{std::cout << "模板函数通用版本打印数据: " << data << std::endl;
}// 模板函数特化,针对char类型
template<>
void printData<char>(char data) 
{std::cout << "模板函数特化版本打印字符: " << data << std::endl;
}int main() 
{int num = 10;char ch = 'A';printData(num);  // 调用非模板函数printData(int)printData(ch);  // 调用模板函数特化版本printData<char>(char)return 0;
}

在这里,对于 printData 函数,有非模板函数、模板函数通用版本以及针对 char 类型的特化版本。调用 printData(num) 时,根据精确匹配优先原则,会调用非模板函数 printData(int);而调用 printData(ch) 时,由于存在针对 char 类型的特化版本,会优先调用这个特化版本,而不是模板函数的通用版本,同样体现了编译器在选择调用函数时遵循的优先匹配规则。

2. 函数隐藏(Function Hiding)

概念
  • 函数隐藏同样出现在类的继承关系中,是指在派生类中定义了与基类同名的函数(不管参数列表是否相同),此时派生类的函数会隐藏基类中同名的所有函数(包括重载函数),在派生类的作用域内,如果不使用作用域限定符显式指定,就无法访问到基类中被隐藏的同名函数。
    隐藏:在基类和派生类之间发生的关系,函数名相同,派生类的函数把基类的函数给隐藏了,只关注函数数名,不管返回值和参数
示例代码
#include <iostream>class Base {
public:void func() {std::cout << "Base类的func函数" << std::endl;}void func(int num) {std::cout << "Base类的func(int)函数" << std::endl;}
};class Derived : public Base {
public:void func(double num) {std::cout << "Derived类的func(double)函数" << std::endl;}
};int main() {Derived derived_obj;derived_obj.func(3.0);  // 调用Derived类的func(double)函数// 以下代码编译错误,因为Derived类的func函数隐藏了Base类的func函数,// 不能直接在Derived类作用域内调用Base类的func函数// derived_obj.func();// 使用作用域限定符可以访问Base类的func函数derived_obj.Base::func();return 0;
}

在上述代码中,Derived 类中定义了 func(double num) 函数,它隐藏了 Base 类中的 func 函数和 func(int num) 函数,所以在 Derived 类的作用域内直接调用 func 函数时,编译器会认为是调用 Derived 类自身定义的函数,如果要访问基类中被隐藏的同名函数,需要通过 Base::func() 这样的作用域限定符来明确指定。

特点
  • 存在继承关系:也是基于类的继承场景出现的情况,派生类定义了与基类同名的函数。
  • 同名即隐藏:只要函数名相同就会发生隐藏,与参数列表是否相同无关,而且是隐藏基类中所有同名函数,这一点和重载、覆盖都不同,重载是通过参数差异来区分不同函数,覆盖是严格按照函数签名一致且有虚函数特性来实现的。
  • 作用域相关访问问题:隐藏导致在派生类作用域内,默认情况下无法直接访问基类中被隐藏的同名函数,需要使用作用域限定符来打破这种隐藏效果,才能调用基类的同名函数。

3. 函数覆盖(重写,Function Overriding)

概念
  • 函数覆盖发生在类的继承关系中,是指在派生类中重新定义了基类中的虚函数,并且要求函数签名(函数名、参数列表、返回类型,返回类型如果是指针或引用类型时允许协变)完全一致(除了 const 修饰符,派生类重写函数可以比基类函数多 const 修饰),当通过基类指针或引用调用该函数时,会根据对象的实际类型(是基类对象还是派生类对象)来决定调用基类的函数还是派生类重写后的函数,这是实现多态性的重要机制。

覆盖:覆盖是隐藏的一种特殊情况,派生类和基类的函数,1 函数名相同;2 返回值相同;3 参数列表相同(不包括this指针在内);4 基类函数为虚函数;即为覆盖。
其原理是子类虚函数表的函数地址覆盖了父类虚函数表里函数的指针。
在这里插入图片描述

示例代码
#include <iostream>class Base {
public:virtual void func() {std::cout << "Base类的func函数" << std::endl;}
};class Derived : public Base {
public:void func() override {std::cout << "Derived类的func函数" << std::endl;}
};int main() {Base* ptr = new Derived();ptr->func();  // 调用Derived类重写后的func函数Base base_obj;base_obj.func();  // 调用Base类的func函数Derived derived_obj;derived_obj.func();  // 调用Derived类的func函数return 0;
}

在这个例子中,Derived 类重写了 Base 类中的虚函数 func,通过基类指针 ptr 指向派生类对象时,调用 func 函数会执行 Derived 类中重写后的版本,体现了多态性。而直接使用基类对象或者派生类对象调用 func 函数时,则分别调用各自类中定义的函数。

特点
  • 存在继承关系:必须在派生类和基类之间发生,基类中声明了虚函数,派生类对其进行重写。
  • 函数签名要求严格一致:函数名、参数列表、返回类型(遵循协变规则等情况除外)要相同,比如基类函数是 void func(int num),派生类重写的函数也得是 void func(int num),目的是让编译器能准确识别这是重写关系,以便在多态调用时正确执行相应版本的函数。
  • 虚函数特性:基类中的函数必须是虚函数(通过 virtual 关键字修饰),这样编译器才会在运行时根据对象的实际类型来动态决定调用哪个类的函数,实现多态行为。

相关文章:

详细全面讲解C++中重载、隐藏、覆盖的区别

文章目录 总结1、重载示例代码特点1. 模板函数和非模板函数重载2. 重载示例与调用规则示例代码调用规则解释3. 特殊情况与注意事项二义性问题 函数特化与重载的交互 2. 函数隐藏&#xff08;Function Hiding&#xff09;概念示例代码特点 3. 函数覆盖&#xff08;重写&#xff…...

一文读懂单片机的串口

目录 串口通信的基本概念 串口通信的关键参数 单片机串口的硬件连接 单片机串口的工作原理 数据发送过程 数据接收过程 单片机串口的编程实现 以51单片机为例 硬件连接 初始化串口 发送数据 接收数据 串口中断服务函数 代码示例 单片机串口的应用实例 单片机与…...

HTML5 网站模板

HTML5 网站模板 参考 HTML5 Website Templates...

mybatis分页插件:PageHelper、mybatis-plus-jsqlparser(解决SQL_SERVER2005连接分页查询OFFSET问题)

文章目录 引言I PageHelper坐标II mybatis-plus-jsqlparser坐标Spring Boot 添加分页插件自定义 Mapper 方法中使用分页注意事项解决SQL_SERVER2005连接分页查询OFFSET问题知识扩展MyBatis-Plus 框架结构mybatis-plus-jsqlparser的 Page 类引言 PageHelper import com.github.p…...

uniapp中rpx和upx的区别

在 UniApp 中&#xff0c;rpx 和 upx 是两种不同的单位&#xff0c;它们的主要区别在于适用的场景和计算方式。 ### rpx&#xff08;Responsive Pixel&#xff09; - **适用场景**&#xff1a;rpx 是一种响应式单位&#xff0c;主要用于小程序和移动端的布局。 - **计算方式**…...

什么是卷积网络中的平移不变性?平移shft在数据增强中的意义

今天来介绍一下数据增强中的平移shft操作和卷积网络中的平移不变性。 1、什么是平移 Shift 平移是指在数据增强&#xff08;data augmentation&#xff09;过程中&#xff0c;通过对输入图像或目标进行位置偏移&#xff08;平移&#xff09;&#xff0c;让目标在图像中呈现出…...

java.net.SocketException: Connection reset 异常原因分析和解决方法

导致此异常的原因&#xff0c;总结下来有三种情况&#xff1a; 一、服务器端偶尔出现了异常&#xff0c;导致连接关闭 解决方法&#xff1a; 采用出错重试机制 二、 服务器端和客户端使用的连接方式不一致 解决方法&#xff1a; 服务器端和客户端使用相同的连接方式&#xff…...

Maven 仓库的分类

Maven 是一个广泛使用的项目构建和依赖管理工具&#xff0c;在 Java 开发生态中占据重要地位。作为 Maven 的核心概念之一&#xff0c;仓库&#xff08;Repository&#xff09;扮演着至关重要的角色&#xff0c;用于存储项目的依赖、插件以及构建所需的各种资源。 了解 Maven 仓…...

隧道网络:为数据传输开辟安全通道

什么是隧道网络&#xff1f; 想象一下&#xff0c;你正在一个陌生的城市旅行&#xff0c;并且想要访问家里的电脑。但是&#xff0c;直接连接是不可能的&#xff0c;因为家庭网络通常受到防火墙或路由器的保护&#xff0c;不允许外部直接访问。这时候&#xff0c;隧道网络&…...

CentOS 7 下 Nginx 的详细安装与配置

1、安装方式 1.1、通过编译方式安装 下载Nginx1.16.1的安装包 https://nginx.org/download/nginx-1.16.1.tar.gz 下载后上传至/home目录下。 1.2、通过yum方式安装 这种方式安装更简单。 2、通过编译源码包安装Nginx 2.1、安装必要依赖 sudo yum -y install gcc gcc-c sudo…...

JAVA 使用apache poi实现EXCEL文件的输出;apache poi实现标题行的第一个字符为红色;EXCEL设置某几个字符为别的颜色

设置输出文件的列宽&#xff0c;防止文件过于丑陋 Sheet sheet workbook.createSheet(FileConstants.ERROR_FILE_SHEET_NAME); sheet.setColumnWidth(0, 40 * 256); sheet.setColumnWidth(1, 20 * 256); sheet.setColumnWidth(2, 20 * 256); sheet.setColumnWidth(3, 20 * 25…...

通过vba实现在PPT中添加计时器功能

目录 一、前言 二、具体实现步骤 1、准备 2、开启宏、打开开发工具 3、添加计时器显示控件 3.1、开启母版 3.2、插入计时器控件 4、vba代码实现 4.1、添加模块 4.2、添加代码 4.3、保存为pptm 5、效果展示 一、前言 要求/目标:在PPT中每一页上面增加一个计时器功能…...

检验统计量与p值笔记

一、背景 以雨量数据为例&#xff0c;当获得一个站点一年的日雨量数据后&#xff0c;我们需要估计该站点的雨量的概率分布情况&#xff0c;因此我们利用有参估计的方式如极大似然法估计得到了假定该随机变量服从某一分布的参数&#xff0c;从而得到该站点的概率密度函数&#x…...

【集成学习】Bagging、Boosting、Stacking算法详解

文章目录 1. 相关算法详解&#xff1a;2. 算法详细解释&#xff1a;2.1 Bagging&#xff1a;2.2 Boosting&#xff1a;2.3 Stacking&#xff1a;2.4 K-fold Multi-level Stacking&#xff1a; 集成学习&#xff08;Ensemble Learning&#xff09;是一种通过结合多个模型的预测结…...

Rabbit Rocket kafka 怎么实现消息有序消费和延迟消费的

在消息队列系统中&#xff0c;像 RabbitMQ、RocketMQ 和 Kafka 这样的系统&#xff0c;都支持不同的方式来实现消息的有序消费和延迟消费。下面我们分别探讨这些系统中如何实现这两种需求&#xff1a; 1. RabbitMQ&#xff1a;实现消息有序消费和延迟消费 有序消费&#xff1…...

【Ubuntu与Linux操作系统:五、文件与目录管理】

第5章 磁盘存储管理 5.1 Linux磁盘存储概述 磁盘存储是Linux系统存储数据的重要组件&#xff0c;它通过分区和文件系统组织和管理数据。Linux支持多种文件系统&#xff0c;如ext4、xfs和btrfs&#xff0c;并以块的形式管理存储设备。 1. 分区与文件系统&#xff1a; 分区&am…...

32_Redis分片集群原理

1.Redis集群分片 1.1 Redis集群分片介绍 Redis集群没有使用一致性hash,而是引入了哈希槽的概念。Redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。 用于将密钥映射到散列插槽的基本算法如下: HASH_SLOT = CRC16(key) mod 16384 集群的每…...

微信小程序mp3音频播放组件,仅需传入url即可

// index.js // packageChat/components/audio-player/index.js Component({/*** 组件的属性列表*/properties: {/*** MP3 文件的 URL*/src: {type: String,value: ,observer(newVal, oldVal) {if (newVal ! oldVal && newVal) {// 如果 InnerAudioContext 已存在&…...

Sql 创建用户

Sql server 创建用户 Sql server 创建用户SQL MI 创建用户修改其他用户密码 Sql server 创建用户 在对应的数据库执行&#xff0c;该用户得到该库的所有权限 test.database.chinacloudapi.cn DB–01 DB–02 创建服务器登录用户 CREATE LOGIN test WITH PASSWORD zDgXI7rsafkak…...

数据结构:LinkedList与链表—面试题(三)

目录 1、移除链表元素 2、反转链表 3、链表的中间结点 4、返回倒数第k个结点 5、合并两个有序链表 1、移除链表元素 习题链接https://leetcode.cn/problems/remove-linked-list-elements/description/ 描述&#xff1a;给你一个链表的头节点 head 和一个整数 val &#xff…...

利用ngx_stream_return_module构建简易 TCP/UDP 响应网关

一、模块概述 ngx_stream_return_module 提供了一个极简的指令&#xff1a; return <value>;在收到客户端连接后&#xff0c;立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量&#xff08;如 $time_iso8601、$remote_addr 等&#xff09;&a…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

USB Over IP专用硬件的5个特点

USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中&#xff0c;从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备&#xff08;如专用硬件设备&#xff09;&#xff0c;从而消除了直接物理连接的需要。USB over IP的…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...