【C++掌中宝】深入理解函数重载:概念、规则与应用

在这里插入图片描述

文章目录

  • 引言
  • 1. 什么是函数重载?
  • 2. 为什么需要函数重载?
  • 3. 编译器如何解决命名冲突?
  • 4. 为什么返回类型不参与重载?
  • 5. 重载函数的调用匹配规则
  • 6. 编译器如何解析重载函数的调用?
  • 7. 重载的限制与注意事项
  • 8. 总结
  • 结语

引言

函数重载是 C++ 中一项强大的特性,它允许程序员在同一作用域内定义多个同名函数,通过不同的参数类型或数量来区分这些函数。函数重载提高了代码的灵活性和可读性,使相同操作在不同上下文中可以使用统一的函数名,从而避免重复定义不同名字的函数。本文将深入探讨函数重载的概念、规则,编译器如何处理重载,以及使用中的注意事项。

1. 什么是函数重载?

在 C++ 中,函数重载是指允许在同一作用域中定义多个具有相同名字但参数列表不同的函数。参数列表可以在参数类型参数数量、或参数顺序上有所区别,而函数返回类型则不会影响函数的重载

在这里插入图片描述

例如,以下是一个简单的 print 函数的重载示例:

#include<iostream>
using namespace std;void print(int i) {cout << "print an integer: " << i << endl;
}void print(string str) {cout << "print a string: " << str << endl;
}int main() {print(12);             // 调用 print(int)print("hello world!");  // 调用 print(string)return 0;
}

在这个例子中,编译器会根据传递的参数类型,自动选择合适的 print 函数来执行。

2. 为什么需要函数重载?

没有函数重载的情况下,每个不同类型的操作都需要一个不同的函数名。例如,在 C 中,如果自己要定义打印不同类型的值的函数,需要定义多个函数如 print_intprint_double 等。随着功能的增加,函数命名会变得非常复杂且难以维护。

函数重载提供了一个优雅的解决方案,让同一个函数名适应多种类型操作,提高了代码的可读性维护性。例如,C++ 中的类构造函数就是依赖函数重载来处理不同参数的初始化。如果没有重载机制,为每种初始化方式命名将非常麻烦。

3. 编译器如何解决命名冲突?

函数重载虽然允许定义多个同名函数,但编译器通过“名称修饰”(Name Mangling)技术来区分每个重载函数。编译时,编译器会根据函数名、参数类型、参数个数对函数名称进行修饰,生成一个唯一的函数标识符

为了了解编译器是如何处理这些重载函数的,我们反编译下上面我们生成的执行文件,看下汇编代码。我们执行命令objdump -d a.out >log.txt反汇编并将结果重定向到log.txt文件中,然后分析log.txt文件。

  • 发现函数void print(int i) 编译之后为:(注意它的函数签名变为——_Z5printi

在这里插入图片描述

  • 发现函数void print(string str) 编译之后为:(注意它的函数签名变为——_Z5printSs

在这里插入图片描述

我们可以发现编译之后,重载函数的名字变了不再都是print!这样做确保了每个函数在编译后具有独特的标识符,从而避免了命名冲突。

同时,返回类型并不会参与函数重载的区分,因为返回值类型不能唯一确定一个函数的调用。

4. 为什么返回类型不参与重载?

比如说下面这个示例:

//返回值不同不能作为重载条件,因为调用时也无法区分
void fxx()
{}int fxx()
{return 0;
}

因为对于有返回值的函数,返回值我可以不接收

返回类型不参与重载的原因是,编译器仅依据函数参数来解析函数调用,而不使用返回值类型。例如:

float sqrt(float);
double sqrt(double);void f(double da) {auto result = sqrt(da);  // 调用 sqrt(double)
}

在没有上下文提示的情况下,编译器无法仅通过返回类型来区分函数。因此,C++ 仅依赖参数列表来处理重载。

5. 重载函数的调用匹配规则

当调用重载函数时,编译器会按照以下顺序依次进行匹配:

  1. 精确匹配:参数类型与声明的函数完全一致,参数匹配而不做转换,或者只是做微不足道的转换,如数组名到指针、函数名到指向函数的指针、T到const T;
  2. 提升匹配:即整数提升(如bool 到 int、char到int、short 到int),float到double
  3. 标准类型转换:如int 到double、double到int、double到long double、Derived到Base、T到void、int到unsigned int;
  4. 用户定义的类型转换:使用类的转换运算符或构造函数进行类型转换。
  5. 省略号匹配:使用 ... 作为可变参数匹配,类似printf中省略号参数。

如果多个函数符合匹配条件且优先级相同,编译器会报错,因为无法确定唯一的最佳匹配。例如:

void f1(char);
void f1(long);void g(int i) {f1(i);  // 模棱两可,编译器无法确定调用 f1(char) 还是 f1(long)
}

6. 编译器如何解析重载函数的调用?

编译器实现调用重载函数解析机制的时候,肯定是首先找出同名的一些候选函数,然后从候选函数中找出最符合的,如果找不到就报错。下面介绍一种重载函数解析的方法:编译器在对重载函数调用进行处理时,由语法分析、C++文法、符号表、抽象语法树交互处理,交互图大致如下:

在这里插入图片描述

这个四个解析步骤所做的事情大致如下:

  • 由匹配文法中的函数调用,获取函数名;
  • 获得函数各参数表达式类型;
  • 语法分析器查找重载函数,符号表内部经过重载解析返回最佳的函数
  • 语法分析器创建抽象语法树,将符号表中存储的最佳函数绑定到抽象语法树上

📌下面比较重要的部分,编译器解析重载函数调用时,主要分为三个步骤:

  1. 确定候选函数集:从当前作用域及其父作用域中,找到所有名称相同的函数。
  2. 筛选可用函数:根据参数类型和数量,筛选出所有参数能够匹配的函数。
  3. 确定最佳匹配:根据函数匹配规则,选出优先级最高的匹配函数。

如果存在多个相同优先级的匹配,编译器会报出“模凌两可”错误。编译器还会尝试从用户定义的命名空间或基类中找到候选函数。

7. 重载的限制与注意事项

  1. 返回类型不能区分重载:仅修改返回类型不会被视为有效的重载。
  2. 默认参数不参与重载选择:默认参数不能作为重载的依据。例如,两个函数仅通过默认参数区分会被视为重复定义。
  3. 运算符重载的限制:不允许为运算符重载提供默认参数。
  4. 避免歧义:当可能出现多个重载函数符合条件时,尽量避免定义过于模棱两可的函数,确保调用时能够明确匹配。

8. 总结

函数重载是 C++ 提供的一项非常实用的特性,它允许我们在同一作用域中定义多个同名函数,从而根据不同类型和数量的参数来实现多态性。通过了解重载的规则和编译器的解析流程,我们可以编写出更加灵活和可维护的代码。

函数重载提高了代码的简洁性和可读性,但也需要注意避免模棱两可的调用情况。正确使用这一特性可以让代码更加优雅、高效。

结语

今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下。

也可以点点关注,避免以后找不到我哦!

Crossoads主页还有很多有趣的文章,欢迎小伙伴们前去点评,您的支持就是作者前进的动力!

在这里插入图片描述

参考:C++的函数重载 - 吴秦 - 博客园 (cnblogs.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1544273.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

柯桥小语种学习之语言交流 | 德语餐厅用语

01 一、入座与点餐 1. Guten Tag! Ein Tisch fr zwei Personen, bitte.&#xff08;你好&#xff01;请给我们一张两人桌。&#xff09; 2. Knnen wir hier sitzen?&#xff08;我们可以坐这里吗&#xff1f;&#xff09; 3. Die Speisekarte, bitte.&#xff08;请给我菜…

在Windows系统上安装的 zlib C++ 库

在Windows系统上安装的 zstd C 库 项目地址步骤步骤一步骤二步骤三如果生成过程中遇到如下错误: 效果 项目地址 https://github.com/madler/zlib 可以发现这个项目有CMakeLists.txt文件,那就比较好搞了 步骤 步骤一 git clone gitgithub.com:madler/zlib.git步骤二 cd zli…

丢失照片/消息/文件,当发现没有备份 Android 手机数据时急救方法

当人们发现他们没有备份 Android 手机数据时&#xff0c;通常为时已晚。但是&#xff0c;我们都不想永久丢失珍贵的照片&#xff0c; 消息和其他文件。这就是为什么您应该检查 遵循 5 大免费 Android 数据恢复工具和最佳替代品 他们。 排名前五的免费 Android 数据恢复软件 1.奇…

黑芝麻A1000-Ubuntu20.04(九)yolov5从训练到板端运行过程详解

宿主机&#xff1a;台式电脑 Ubuntu20.04 开发板&#xff1a;A1000&#xff08;烧录版本SDK v2.3.1.2&#xff09; 模型转换容器&#xff1a;bsnn-tools-container-stk-4.2.0 编译容器&#xff1a;a1000b-sdk-fad-2.3.1.2 yolov5使用工程&#xff1a;黑芝麻根据https://github.…

PHP探索校园新生态校园帮小程序系统小程序源码

探索校园新生态 —— 校园帮小程序系统&#xff0c;让生活更精彩&#xff01; &#x1f331;【开篇&#xff1a;走进未来校园&#xff0c;遇见新生态】&#x1f331; 你是否厌倦了传统校园的繁琐与单调&#xff1f;是否渴望在校园里也能享受到便捷、智能的生活体验&#xff1…

3d可视化图片:通过原图和深度图实现

1、depthy 在线体验demo: https://depthy.stamina.pl/#/ 也可以docker安装上面服务: docker run --rm -t -i -p 9000:9000 ndahlquist/depthy http://localhost:90001)首先传原图 2)再传对应深度图 3)效果 </ifra

网络事件管理

网络事件管理是运行组织 IT 网络不可或缺的一部分&#xff0c;网络事件管理的最终目标很简单&#xff1a;在发生中断时尽快恢复服务或功能。但是为了高效和一致地进行&#xff0c;IT 运营团队需要时刻保持警惕&#xff0c;不断了解网络事件&#xff0c;并且必须系统地遵循一套程…

opencv4.5.5 GPU版本编译

一、安装环境 1、opencv4.5.5 下载地址&#xff1a;https://github.com/opencv/opencv/archive/refs/tags/4.5.5.ziphttps://gitee.com/mirrors/opencv/tree/4.5.0 2、opencv-contrib4.5.5 下载地址&#xff1a;https://github.com/opencv/opencv_contrib/archive/refs/tags/4…

ToB项目身份认证AD集成(二):一分钟搞定window server 2003部署AD域服务并支持ssl加密(多图保姆教程+证书脚本)

在ToB的应用开发中&#xff0c;往往需要集成AD域控实现身份认证&#xff0c;同时也算是近期工作的总结&#xff0c;之前已介绍了基础的AD、Ldap&#xff0c;本文主要介绍如何大家一个本地的测试环境。 相关系列&#xff1a; ToB项目身份认证AD集成&#xff08;一&#xff09;&a…

【JavaSE】-- 类和对象(1)

文章目录 1. 面向对象的初步认知1.1 什么是面向对象1.2 面向对象与面向过程 2. 类的定义和使用2.1 简单认识类2.2 类的定义格式 3. 类的实例化3.1 什么是实例化3.2 类和对象的说明 4. this引用4.1 为什么要有this引用4.2 什么是this引用4.3 this引用的特性 5. 对象的构造及初始…

增强GPT4v的Grounding能力,video-level

开源链接&#xff1a; appletea233/AL-Ref-SAM2: AL-Ref-SAM 2: Unleashing the Temporal-Spatial Reasoning Capacity of GPT for Training-Free Audio and Language Referenced Video Object Segmentation (github.com) In this project, we propose an Audio-Language-Refe…

Spring Boot中实现一个递归获取省市区行政区划代码

Spring Boot中实现一个递归获取省市区行政区划代码 写于20240924 10:23 在Spring Boot中实现一个递归获取省市区行政区划代码的功能&#xff0c;可以按照以下步骤进行。我们将使用Spring Data JPA来与数据库交互&#xff0c;并构建一个递归的方法来获取层级数据。 首先这里数据…

11周年 | 初心不改,焕新前行,奔赴下一个10年!

2024年8月13日&#xff0c;爱加密正式迎来了11岁生日&#xff0c;在爱加密肩负着崇高使命踏浪而行的10年间&#xff0c;蓝绿色的品牌标识一直伴于左右。随着时代的变迁以及市场需求的不断变化&#xff0c;企业同样也需要在品牌上做出创新递进&#xff0c;从而更加适应市场竞争的…

数据科学的秘密武器:defaultdict——Python字典的自动化填充神器,让数据结构更灵活

目录 什么是defaultdict 引入动机 创建与初始化 工作原理 自定义默认值函数 注意事项 使用案例 使用场景 1: 计数 使用场景 2: 分组数据 使用场景 3: 嵌套字典结构 进阶案例使用 进阶案例 1: 使用 defaultdict 实现词频统计并排序 进阶案例 2: 使用 defaultdict 实…

OpenCSG推出StarShip SecScan:AI驱动的软件安全革新

OpenCSG 导读 如今&#xff0c;IT 技术迅速发展&#xff0c;软件安全不仅是企业稳健运营的基础&#xff0c;更是整个社会经济体系安全的保障。加强软件安全&#xff0c;尤其是在开发阶段识别和修补漏洞&#xff0c;是企业必须重视的问题。国际数据公司&#xff08;IDC&#xf…

MyBatis 入门教程-搭建入门工程

Maven作为一个优秀的项目构建和管理工具,在日常的开发中被大多数开发者使用,后续的项目也是基于Maven来构建。 创建一个Maven项目 利用IDEA创建项目工具来创建一个Maven项目 添加MyBatis的依赖 这里可以从Maven仓库地址中进行查看, https://mvnrepository.com/ 从这里可…

反汇编—switch

x64和x86分析类似 标号1的位置要计算出&#xff1a;减去(debug) / 加上(release)第一个case要等于0&#xff0c;因为第一个case在跳转表数组的0下标位置 通过1和2&#xff0c;可以知道它们应该是连续case&#xff0c;还要判断是否缺项&#xff0c;进入跳转表看 可以看到原本应…

经济型伺服电缸EMB系列

经济型伺服电缸系列特点 小型电缸&#xff0c;推力范围:5kg-1500kg 精巧设计 所有部件模块化组合&#xff0c;标准化&#xff0c;经济化 轧制滚珠丝杠&#xff0c;高效率&#xff0c;高速度 匹配经济型步进伺服电机驱动器一体化&#xff0c;可总线 can&#xff0c;erthercat等&…

NAS求变,“0成本、低门槛”的鲁大师能否脱颖而出?

互联网科技的高速发展&#xff0c;推动了全球信息爆炸的进程。如何高效地存储和使用这些海量数据成了困扰企业、乃至个人的一大难题。从U盘、到移动硬盘、再到各种网云盘、以及愈发大众化的NAS……存储解决方案也随着个人及家庭数据存储需求的不断增长而发展着。如今&#xff0…

shardingjdbc-读写分离配置

文章目录 1、application.yml2、shardingsphere.yaml3、创建实体类 User4、创建 UserMapper5、添加依赖6、读写分离测试7、事务测试 我们的主从复制已经提前搭建好&#xff1a; mysql-搭建主从复制&#xff1a;https://blog.csdn.net/m0_65152767/article/details/142214434 1…