21天学会C++:Day4----函数重载

news2024/10/5 14:35:48

· CSDN的uu们,大家好。这里是C++入门的第四讲。
· 座右铭:前路坎坷,披荆斩棘,扶摇直上。
· 博客主页: @姬如祎
· 收录专栏:C++专题

目录

1. 知识引入

2. 函数重载的知识点

2. 为什么C语言不支持函数重载而C++支持呢?

2.1 前置知识

2.2 原因详解


 

1. 知识引入

在书写排序排序算法时,我们经常会用到 Swap(int* a, int* b) 函数,可是在实际的运用中,我们不可能只会遇到交换整型数据的情况。当我们需要交换其它类型的数据时,用C语言来实现就比较麻烦了,你必须写很多个函数名不同的函数,来区别不同数据类型之间两个数的交换。

void SwapInt(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void SwapDouble(double* a, double* b)
{
	double tmp = *a;
	*a = *b;
	*b = tmp;
}

这样是不是十分的麻烦,于是C++引入了一个新的语法:函数重载:

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

于是,实现相同的功能C++的代码就可以这么写:

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

void Swap(double* a, double* b)
{
	double tmp = *a;
	*a = *b;
	*b = tmp;
}

这样写的好处是显而易见的:我们交换数据只需要记忆Swap这一个函数就可以了。而不需要像C语言那样对于不同的数据类型记忆不同的函数。

2. 函数重载的知识点

再来看看函数重载的概念:

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

下面我们枚举一些能够发生函数重载的例子:

#include<iostream>
using namespace std;

// 1、参数类型不同
int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}

double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}


// 2、参数个数不同
void f()
{
    cout << "f()" << endl;
}
void f(int a)
{
    cout << "f(int a)" << endl;
}


// 3、参数类型顺序不同
void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
}

void f(char b, int a)
{
    cout << "f(char b, int a)" << endl;
}

int main()
{
    Add(10, 20);
    Add(10.1, 20.2);
    f();
    f(10);
    f(10, 'a');
    f('a', 10);
    return 0;
}

注意第三点一定是参数类型的顺序不同,千万别想成形参不同哈。

这里就会有一个问题了,为什么函数的返回值不能构成函数重载呢?

看下面的一段代码:f() 这两个函数仅有返回值不同,在main函数调用 f() 时, 请问,我们应该调用哪一个函数呢?显然这是不正确的代码!

int f()
{
	cout << "int f()" << endl;
    return 1;
}

double f()
{
	cout << "double f()" << endl;
    return 1.1;
}

int main()
{
	f();
	return 0;
}

我们结合一下上一讲的缺省参数。像这样的代码是会报调用不明确的错误的。

c4f84877f880498daa1a3cb8ce252a17.png

 以上就是函数重载的全部知识点。

2. 为什么C语言不支持函数重载而C++支持呢?

2.1 前置知识

在搞清楚这个问题之前,我们还需要再回忆C / C++ 的编译环境。编译环境可以分为编译,链接两个阶段。编译又可以细分为:预编译(预处理),编译,汇编三个阶段。

预编译(预处理):这个阶段的任务包括了头文件的展开,注释的删除,条件编译,宏的替换等等,源文件经过预处理会生成一个以.i为后缀的文件。但是在VS这个集成开发环境是无法使整个编译过程停到这一步的,我们需要在Linux环境下才能观察到。

输入命令:

gcc -E add.c -o add.i

就能看到源文件经过预编译(预处理)之后的文件了。

编译:这个阶段的任务就是对生成的. i文件进行语法分析,词法分析,符号汇总等,最终形成汇编代码。这里的符号汇总会将那些可能跨文件使用的符号进行汇总,包括全局变量,函数名等等。完成编译阶段会生成一个以.s的后缀的文件。

可以在Linux环境中输入命令查看:

gcc -S add.i

汇编:将汇编代码转化成机器指令(二进制指令),形成符号表,符号表中包含了符号的名称,符号的地址等。假设一个源文件中只有函数的定义,那么符号表中的该符号对应的地址就是一个无效的地址。经过汇编阶段会生成一个后缀名为. o的文件。在Linux环境下,. o文件,以及最终生成的可执行文件都是Elf格式的文件。

可以输入命令查看符号表:

readelf -s add.o

这里的.o文件你可以类比VS中的目标文件.obj。

链接:这个阶段的任务是合并段表,符号表的合并与重定位。因为我们写的源文件在编译阶段都是分开编译的,他们联系起来就是在链接阶段。例如:当函数的声明与定义是分文件编写的时候,如果只有函数的声明,却没有函数的定义,就会报链接错误:

 LNK:link的缩写表明是链接时出现的错误。

2.2 原因详解

本质的原因就是在与C语言与C++的函数名修饰规则不同,即是添加到符号表里面的函数的符号不同。怎么查看符号表里面的函数符号嘞,可以通过看汇编代码查看,也可以通过直接看符号表查看。这里我们就演示第一种。

我们先来看C语言的函数名修饰规则:

#include<stdio.h>

int Add(int x, int y)
{
    return x + y;
}

int main()
{
    int ret = Add(2, 3);
    return 0;
}

将上面的代码通过gcc编译器编译,得到生成的可执行文件:testc,然后输入命令查看汇编代码:

# 编译,指定可执行文件为:testc
gcc test.c -o testc

# 查看汇编代码
objdump -S testc

然后找到Add函数,就可以看到C语言的函数经过函数名修饰规则之后的符号!

 我们可以看到C语言用的函数的符号就是函数名。当我们定义两个同样名字的函数,在编译的过程中编译器尝试为函数生成二进制代码时,编译器无法确定使用哪个函数的定义,从而导致编译出错。

我们再来看g++编译器下的C++的函数名修饰规则:

#include<iostream>
using namespace std;

int Add(int x, int y)
{
    return x + y;
}

double Add(double x, double y)
{
    return x + y;
}

int main()
{
    int ret = Add(2, 3);
    int ans = Add(2.2, 3.3);
    return 0;
}

将上面的代码通过g++编译器编译,得到生成的可执行文件:testcpp,然后输入命令查看汇编代码:

# 编译,指定可执行文件为:testcpp
g++ test.c -o testcpp

# 查看汇编代码
objdump -S testcpp

然后找到两个不同的Add函数,就可以看到C++的函数经过g++编译器函数名修饰规则之后的符号! 

 g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。如上图第一个函数,_Z:固定的格式,3:Add函数的函数名长度是3,i:Add函数的第一个参数为int,缩写为i,i:Add函数的第二个参数还是int,同样缩写为i。

我们可以看到VS编译器的函数名修饰规则要复杂得多。

在编译时,因为函数的符号并不相同,编译器就会生成不同的二进制代码。在函数调用的地方,也会根据传入参数的不同调用不同的函数。

这便是C++能够函数重载C语言不行的原因。

这里有人可能会问,我们将函数的返回值添加到函数名修饰规则是不是通过函数的返回值也能实现函数重载了呢?

显然是不行的,假设我们有两个仅返回值不同的函数,在函数调用的地方,编译器无法确定是调用的是哪一个函数,会出现调用歧义。

 

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

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

相关文章

springboot贫困生勤工助学评定管理系统

本系统尝试使用springboot在网上架构一个动态的贫困生管理系统&#xff0c;以使每一用户在家就能通过系统来进行贫困生管理。 Spring Boot 是 Spring 家族中的一个全新的框架&#xff0c;它用来简化Spring应用程序的创建和开发过程。也可以说 Spring Boot 能简化我们之前采用S…

数据结构学习记录——图应用实例-拯救007(问题描述、解题思路、伪代码解读、C语言算法实现)

目录 问题描述 解题思路 伪代码 总体算法 DFS算法 伪代码解读 总体算法 DFS算法 具体实现&#xff08;C语言&#xff09; 问题描述 在老电影“007之生死关头”&#xff08;Live and Let Die&#xff09;中有一个情节&#xff0c;007被毒贩抓到一个鳄鱼池中心的小岛…

Matlab - Plot in plot(图中画图)

Matlab - Plot in plot&#xff08;图中画图&#xff09; 这是在MATLAB中创建一个嵌入式图形的示例&#xff0c;可以在另一个图形中显示。 与MATLAB中的“axes”函数相关。 Coding % Create data t linspace(0,2*pi); t(1) eps; y sin(t);% Place axes at (0.1,0.1) with w…

学系统集成项目管理工程师(中项)系列24a_信息系统集成专业技术知识(上)

1. 信息系统的生命周期 1.1. 【19下选10】 1.2. 立项 1.2.1. 形成《需求规格说明书》并确定立项 1.2.1.1. 【21上选11】 1.3. 开发 1.3.1. 【22下选10】 1.3.2. 以立项阶段所做的需求分析为基础&#xff0c;进行总体规划。之后&#xff0c;通过系统分析、系统设计、系统…

ChatGPT4 镜像网站推荐

文章目录 1. TomChat2. Ai Doge3. 二狗问答4. 小莓用AI5. Ora6. ChatGPT镜像7. ChatGPT镜像8. VIVI-AI9. 小杰AI10. ChatGPT Web11. AIchatOS 什么是ChatGPT? ChatGPT&#xff0c;全称&#xff1a;聊天生成预训练转换器&#xff08;英语&#xff1a;Chat Generative Pre-train…

辅助驾驶功能开发-功能规范篇(16)-2-领航辅助系统NAP-HMI人机交互

书接上回 2.3.7HMI人机交互 2.3.7.1显示 (1)图标 序号 图标状态 (图形、颜色供参考) 含义说明 备注 1 辅助驾驶功能READY

Winform窗体利用WebApi接口实现ModbusTCP数据服务

在上位机开发过程中&#xff0c;有时候会遇到需要提供数据接口给MES或者其他系统&#xff0c;今天跟大家分享一下&#xff0c;如何在Winform等桌面应用程序中&#xff0c;开发WebApi接口&#xff0c;提供对外modbus设备的数据服务。通讯模型是&#xff1a; 为了更好地演示应用场…

华为OD机试真题 Java 实现【简单的解压缩算法】【2023Q1 200分】

一、题目描述 现需要实现一种算法&#xff0c;能将一组压缩字符串还原成原始字符串&#xff0c;还原规则如下&#xff1a; 1、字符后面加数字N&#xff0c;表示重复字符N次。例如&#xff1a;压缩内容为A3&#xff0c;表示原始字符串为AAA。 2、花括号中的字符串加数字N&…

MyBatis环境搭建+第一个MyBatis程序

目录 1.MyBatis是什么&#xff1f; 2.MyBatis开发环境搭建 3.我的第一个MyBatis程序 1.MyBatis是什么&#xff1f; MyBatis是一款数据库框架&#xff0c;是一款优秀的持久层框架&#xff0c;它不仅支持用户自定义SQL和存储过程&#xff0c;而且还具有高级映射功能。简单来说…

【重新定义matlab强大系列九】函数isoutlier查找数据中的离群值

&#x1f517; 运行环境&#xff1a;Matlab &#x1f6a9; 撰写作者&#xff1a;左手の明天 &#x1f947; 精选专栏&#xff1a;《python》 &#x1f525; 推荐专栏&#xff1a;《算法研究》 #### 防伪水印——左手の明天 #### &#x1f497; 大家好&#x1f917;&#x1f91…

【TikZ 简单学习(上):基础绘制】Latex下的绘图宏包

【TikZ 简单学习[上]基础绘制】Latex下的绘图宏包 前置简单图形绘制基本架构路径绘制添加样式/风格弧线绘制剪切抛物线和正弦曲线绘制填充和绘制渲染绘制箭头循环添加文本信息绘制一个角度 前置 Latex 可以解决绘制这些东西&#xff1a; ∫ a b 1 x d x \int_a^b\frac{1}{x}dx…

DoFE:Domain-oriented Feature Embedding

key : 通过利用多源领域的知识来提高CNN在未见目标领域上的泛化能力。 我们的DoFE框架通过动态丰富图像特征与来自多源领域学习的附加领域先验知识相结合&#xff0c;使语义特征更具辨别性。引入了一个领域知识池来学习和记忆从多源领域提取的先验信息。 然后&#xff0c;原始…

Raidrive安装配置,结合alist实现将webdav网盘挂载为本地磁盘(保姆级教程)

目录 1. 下载安装2. 添加网盘3. 常见报错3.1 不要勾选安全连接3.2 路径必须填写正确 4. 测试效果总结 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 欢迎关注 『发现你走远了』 博客&#xff0c;持续更新中 书接上文 AList挂载工具安装搭建使用教程&#xff0c;快速访…

【数据结构】一文读懂循环队列的实现细节

循环队列最早出现在计算机系统设计中&#xff0c;它的出现主要是为了满足实际需求&#xff1a;在存储机制上&#xff0c;传统的队列存储方式难以满足一些实际应用中需要存储大量数据的场景。在有限的数组空间内&#xff0c;传统的队列存储方式可能会出现存储空间浪费过多、存储…

【操作系统】线程简介

线程简介 线程概念 在许多经典的操作系统教科书中&#xff0c;总是把进程定义为程序的执行实例&#xff0c;它并不执行什么, 只是维护应用程序所需的各种资源&#xff0c;而线程则是真正的执行实体。 所以&#xff0c;线程是轻量级的进程&#xff08;LWP&#xff1a;light w…

4.1 - 信息收集 - 子域名收集

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 子域名收集 一、域名爆破工具二、搜索引擎1、百度2、必应 三、第三方网站1、VirusTotal2、…

LLaMA模型系统解读

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

深度剖析,如何从底层代码层面理解Selenium和Appium的关联

目录 前言&#xff1a; 一、Selenium和WebDriver 二、Appium和WebDriver 三、Selenium和Appium的底层关联 1. Selenium WebDriver提供底层的浏览器控制机制 2. 利用JSON Wire Protocol通信协议实现通讯机制 四、实例代码 总结&#xff1a; 前言&#xff1a; Selenium和…

FFmpeg命令实战(中)

标题 1.ffplay命令播放2.ffplay简单过滤器3 .ffmpeg命令参数1.主要参数2. 音频参数3.视频参数 4.ffmpeg命令提取音视频数据1.保留封装格式2.提取视频3.提取音频 5.ffmpeg提取像素格式1.提取YUV2.提取RGB3.提取PCM 5.ffmpeg命令转封装格式1.保持编码格式2.改变编码格式3.修改帧率…

String源码

介绍 1&#xff09;String 是一个 final 类&#xff0c;即不能被继承的类 。 2&#xff09;String类实现了 java.io.Serializable 接口&#xff0c;可以实现序列化。 3&#xff09;String类实现了 Comparable< String>&#xff0c;可以用于比较大小&#xff08;按顺序…