在C上++ -- 函数重载与引用

news2024/9/20 10:53:09

一、函数重载

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

简单来说:C语言中是不允许同名函数出现的,而在C++中,如果这些函数的参数不同(包括参数个数、参数的顺序、参数的类型),就允许同名函数的出现。在编译过程中,编译器就通过参数进行自动识别,并且调用你想使用的函数。

1、参数类型不同
int Add(int left, int right)
double Add(double left, double right)

// 2、参数个数不同
void f()
void f(int a)

// 3、参数类型顺序不同
void f(int a, char b)
void f(char b, int a)

参数不同的意义是让编译器能够通过参数准确的识别出要调用的函数,因此我们不能使其出现歧义。

void f(int a,int b)
void f(int left, int right)

这个函数,看似参数不同,但实际上都是int类型。当我们调用它们,如f(1,1),编译器是识别不出来我们具体想要调用哪个的,即会出现歧义,导致错误。

void f()
void f(int a=10)

这个函数比较特殊,因为参数不同,所以构成函数重载。但是由于缺省参数的存在,当我们调用f()时,会产生歧义,无法区分。

函数名修饰规则:

名字修饰本质上来说就是编译器识别不同参数的手段。

在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

这样一来,虽然我们写代码时函数名字相同,但是在编译器看来,实际上是几个不同的函数,对应着不同的地址。

注意:参数相同,返回值不同时不构成重载。因为我们调用的时候,一定会传入参数,而不会传入返回值,编译器就无法区分了。因此编译器的名字修饰,采用的都是对参数的判断。

 

 编译的时候,test.s中,只有头文件,即函数的声明,和调用函数的代码。而函数的定义是在Stack.s中的,要在链接阶段才能找到地址。

编译进行符号汇总(全局符号、函数),汇编生成符号表,在链接时,通过符号表去找地址,完成符号表的重定位。

反汇编中,两个函数的地址不同。

 

 

 

 

g++编译器,利用参数,将函数名进行区分。 

执行程序时,call调用就能清楚区分不同函数了。 

二、引用

1、引用的概念:

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。

2、引用的特点:

1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体

int a=10;
int& b=a;//引用初始化
int& c =b//多个引用
//此时b  c  均为a的别名,改变一个其它2个都改变

int x=100;

b=x;//此时为赋值

3、引用作参数(类似于指针)

引用做参数  与指针的某些功能类似。

1、输出型参数

可以通过形参直接改变实参。

如LeetCode中的returnsize,指针时可以利用解引用操作符,*returnsize=x。

当returnsize参数为引用时,直接通过returnsize=x,可以直接修改外面的returnsize

void Swap(int& left,int& right)
{
	int tmp = left;
	left = right;
	right = tmp;
}

Swap函数交换值时,用引用作为参数,可以直接修改外面的left和right。

2、提高效率  

大对象/深浅拷贝

我们知道,在外面传入参数后,在函数内部往往需要拷贝一份,作为临时变量/形参。

当拷贝的对象内存较大或者是深浅拷贝时,拷贝的操作就有一定的消耗了。

使用指针时,我们可以传入地址,通过解引用来访问,只需要拷贝一个指针即可,消耗很小。

使用引用时,我们直接用相同类型的引用作为形参,直接用别名来访问,消耗很小,提高了效率。

总结:引用作为参数时,没有使用的限制条件,可以用来作为输出型参数,还可以有效提高效率,理解上与指针和取地址、解引用操作类似。

4、引用作返回值

1、提高效率

int Count1()
{
	static int n = 0;
	n++;
	return n;
}
int Count3()
{
	int n = 0;
	n++;
	return n;
}

对于这两个函数,无论变量是否用static修饰(本质上是存储在栈区还是静态区),返回值都是int类型的。因为使用函数时会涉及到函数栈帧的创建与销毁,对于除了引用外的其它类型(C类型)编译器会统一创建一个临时变量用来存储返回值(不区分是否是static修饰),小对象可能是寄存器register,大对象就是创建变量了。

 创建临时变量的过程额外需要拷贝一次,从而降低了效率。

使用指针时,返回地址,通过解引用找到函数中创建的那个变量,然后进行后续操作。

还可以用引用作为返回值,引用是别名,可以直接通过别名找到函数中创建的变量,不用额外拷贝一次,提高了效率。

2、越界风险

int& Count2()
{
	static int n = 0;
	n++;
	return n;
}
int& Count4()
{
	int n = 0;
	n++;
	return n;
}
int& Count5(int x)
{
	int n = x;
	n++;
	return n;
}

对于Count2函数,创建的n是被static修饰的,在静态区创建的,无论Count2的函数栈帧是否销毁或清理,它都是存在的,也可以正常访问。

 先用一个ret1,简单接收一下Count2返回值,也就是一个简单的赋值操作,将n的别名赋值给它。

再调用一次Count2函数,静态区的n的值变为2,返回别名,再创建一个别名ret2来接收,此时ret2也就是n的别名,其值也为2。

因为保存在静态区,无论栈帧是否清理,均不变,且安全不越界。

 对于Count4函数

 Count4中的n是创建在栈区的,是局部的,我们用一个ret2作为别名,可以对其访问。

第一次打印ret2,其栈帧还未被清理,因此还为1.

调用test函数清理栈帧后,再次调用ret2,就变成随机值了。

并且由于Count4栈帧已经销毁,此时我们对ret2的访问实际上已经越界了,只是编译器没有报错

对于Count5函数

 第一次调用ret的值为11,然后栈帧销毁。第二次调用,由于函数相同,栈帧也相同,参数变为20,ret作为别名的位置相同,最终结果为21。

总结:引用返回局部对象,结果不确定,会越界访问(与返回栈空间地址类似)。在堆区、静态区使用引用作返回值是安全的。是否出现随机值,与编译器有关,是否会在销毁栈帧后立马清理栈帧。(与指针返回地址类似,可能返回的地址的权限已经还给操作系统了)

3、简化读写操作


 对于C语言的顺序表,修改和得到值需要不同的函数。使用引用操作,可以得到要操作那个位置数据的别名,然后可以对这个别名进行  读取/打印   写入/修改  操作

 5、常引用

1、变量之间

权限可以缩小或平移,但是不能放大
	const int a = 10;//int a=10;
	int& ra1 = a; // 该语句编译时会出错,a为常量,权限放大了,ra可以修改a
	const int& ra2 = a;//权限的平移,若a仅为int,则为缩小

引用过程可以看作本体和分身的关系。本体的权限一定是>=分身的。

若本体a没有const修饰,它可以随意修改,若有一个引用的分身ra1,也没有const修饰,此时它们权限相同。若另一个引用的分身ra2被const修饰了,那么它不能修改,即不能进行类似ra2++的操作。这个const,缩小的是ra2作为分身/别名的权限,而不是本体/本身a的权限。

2、对常量引用

 常量不能被修改,别名必须用const来修饰。

3、涉及类型转换的引用

普通的类型转换

 

这个临时变量具有常属性

当将i给别名ri初始化时,没有类型转换,不会报错。

当用d给rri初始化时,先进行类型转换,生成一个值为6,具有常属性的临时变量,临时变量都可看作右值,包括之前的Count1、Count3函数,此时若想赋值,rri前必须要加上const修饰。 

总结:引用的知识从以下几点考虑。

引用作为参数,引用作为返回值,返回值的对象的栈帧是否被销毁/清理,是否存在。

是否用引用来接收,接收的值是否具有常属性,是否需要加const来修饰。

引用与指针的相似之处,引用可能导致的越界问题,引用对于数据读写在代码层面上的优化。

引用和指针的不同点:

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体
4. 没有NULL引用,但有NULL指针
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全
 

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

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

相关文章

点云处理及三维重建软件(Point Cloud Viewer, PCV)的设计与实现

GitHub 地址:point-cloud-viewer 文章目录 使用教程以及相关工具库Step 1 搭建环境Step 2 使用Cmake构建工程Step3 使用VS 编写code并编译执行 点云处理及三维重建软件(PCV)的设计与实现一, 软件总体设计1.1 软件设计流程需求分析总体设计技术选型详细设…

【RestFul系列】RestFul学习笔记

目录 一、REST定义 二、REST架构的主要原则 三、RESTful介绍 1、资源(Resources) 2、 表现层(Representation) 3、 状态转化(State Transfer) 四、RESTful的使用 1、RESTful资源操作 2、接口示例&…

verilog设计实现8b-10b编码器包括3b4b,5b6b 及modelsim仿真

下面是8b10b编码器的设计步骤。 确定数据输入和输出接口。例如,您需要确定8位并行数据输入和10位串行数据输出。 计算数据带宽。这与芯片中可用的时钟速度密切相关。 选择编码表。根据应用选择最佳编码表,8b10b编码器至少应使用一张编码表。详细了解和选择编码表有利于改善编…

图的存储及基本操作总结(邻接矩阵、邻接表)及C/C++代码实现

文章目录 前言一、邻接矩阵1.概念2.图像示例3. 代码实现注意邻接矩阵的特点 二、邻接表1.概念2.图像示例3.代码实现邻接表的特点 前言 图是一种比较复杂的数据结构,每个结点之间可以有多种关系。 所以,一个图可以呈现出千奇百怪的形式。 对于不同的形式…

使用FFMPEG库将YUV编码为H264

准备 ffmpeg 4.4 p准备一段yuv420p的格式的视频原始数据 这里我们使用命令直接提取 ffmpeg -i .\beautlWorld.mp4 -pixel_format yuv420p -s 1280x720 yuv420p_1280x720.yuv 编码流程 大致可以分为以下几步: 1.初始化编码器并设置参数 2.初始化AVPacket和AVFr…

【Java基础 1】Java 环境搭建

🍊 欢迎加入社区,寒冬更应该抱团学习:Java社区 📆 最近更新:2023年4月22日 文章目录 1 java发展史及特点1.1 发展史1.2 Java 特点1.2.1 可以做什么?1.2.2 特性 2 Java 跨平台原理2.1 两种核心机制2.2 JVM…

Activiti入门

目录 一、了解工作流 1、什么是工作流 2、工作流引擎 3、常见工作流引擎 4、Activiti7概述 4.1、Activiti介绍 4.2、建模语言BPMN 4.3、Activiti使用流程 一、了解工作流 1、什么是工作流 工作流(Workflow),就是通过计算机对业务流…

SSeg总体思路

1:在train中加载train和test数据集。 2:指定数据集为kitti,确定训练验证的batchsize。 3:提前定义好数据预处理,首先是几何变换,包括随机裁切等。 4:然后是外观变换,包括高斯滤波…

计算同列排斥力的一种可能方法

假设神经网络同列数字之间有一种排斥力,且这种排斥力也与距离的平方成反比。设0是环境,1是粒子,则两个1之间的排斥力就是距离平方的倒数。 考虑任意遥远的两个粒子之间都有排斥力,可以得到同列排斥力的计算方法为 如计算"01…

移动硬盘数据恢复软件实用技巧

在我们日常生活中,移动硬盘已经成为了我们不可或缺的存储设备之一。但是,由于各种原因,移动硬盘中的数据有时会丢失或损坏,这时候我们就需要使用移动硬盘数据恢复软件来帮助恢复数据。那么,移动硬盘数据恢复软件有哪些…

Windows环境下实现设计模式——中介者模式(JAVA版)

我是荔园微风,作为一名在IT界整整25年的老兵,今天总结一下Windows环境下如何编程实现中介者模式(设计模式)。 不知道大家有没有这样的感觉,看了一大堆编程和设计模式的书,却还是很难理解设计模式&#xff…

modbus指令测试

目录 一.抓包二.modbus与plc三.usb包分析四.编写modbus指令测试五.调试工具 一.抓包 1.串口抓包?wireshark!: https://xuxeu.github.io/uart-catch/ 2.Windows&Linux USB抓包方法总结:https://zhuanlan.zhihu.com/p/267820933 3.USB The Setup Pack…

(“树” 之 前中后序遍历 ) 94. 二叉树的中序遍历 ——【Leetcode每日一题】

基础概念:前中后序遍历 1/ \2 3/ \ \ 4 5 6层次遍历顺序:[1 2 3 4 5 6]前序遍历顺序:[1 2 4 5 3 6]中序遍历顺序:[4 2 5 1 3 6]后序遍历顺序:[4 5 2 6 3 1] 层次遍历使用 BFS 实现,利用的就是 BFS…

一文搞懂Java中的异常问题

思考几个问题 1:JavaWeb系统中,我的代码未做任何处理,报错了还会往下执行吗? 2:JavaWeb系统中,我的代码做了 try catch finally, 报错了还会往下执行吗? 3:JavaWeb系统中&#xff0c…

软考高频考点--《项目采购管理》

现在离2023年上半年软考还有一个多月的时间,相信各位小伙伴们已经进入紧张的备考状态了。 小编今天为大家整理了软考的一些高频考点–《项目采购管理》,希望对正在备考软考的你有所帮助! 采购是从项目团队外部获得产品、服务或成果的完整的购…

alsa_lib移植到IMX6ULL

简介 ALSA是Advanced Linux Sound Architecture的缩写,目前已经成为了linux下的主流音频体系架构,提供了音频和MIDI的支持。 交叉编译alsa_lib和alsa_utils 下载alsa_lib 在官网中下载AlsaProject 编译 先将文件解压,然后进入alsa_lib…

运筹说 第94期|论文速读之基于关键路径的置换流水车间调度问题

前几期的推送已经讲解了网络计划的基本知识、数学模型和相关算法,相信大家对网络计划已经有了充分的了解,这期小编将带大家一起来读一篇基于关键路径的置换流水车间调度问题的文章。 1.文章信息 题目:An efficient critical path based meth…

手把手教你分析解决MySQL死锁问题

在生产环境中出现MySQL死锁问题该如何排查和解决呢,本文将模拟真实死锁场景进行排查,最后总结下实际开发中如何尽量避免死锁发生。 一、准备好相关数据和环境 当前自己的数据版本是8.0.22 mysql> select version; ----------- | version | --------…

Arduino 多任务软件定时器:Simpletimer库的使用

Arduino 多任务软件定时器:Simpletimer库的使用 📌Simpletimer库Arduino官方介绍信息:https://playground.arduino.cc/Code/SimpleTimer/✨该库也是利用了millis()函数来实现任务轮询的。与之类似的还有ESP8266固件自带的Ticker库,但是Ticker库使用仅限于ESP8266内调用,Si…

Discourse Google Analytics 3 的升级提示

根据 Google 官方的消息: Google Analytics(分析)4 是我们的新一代效果衡量解决方案,即将取代 Universal Analytics。自 2023 年 7 月 1 日起,标准 Universal Analytics 媒体资源将停止处理新的命中数据。如果您仍在使…