C++——基础语法(2):函数重载、引用

4. 函数重载

        函数重载就是同一个函数名可以重复被定义,即允许定义相同函数名的函数。但是相同名字的函数怎么在使用的时候进行区分呢?所以同一个函数名的函数之间肯定是要存在不同点的,除了函数名外,还有返回类型和参数两部分可以视为函数的特征。

        以返回类型来区别,因为在函数调用时不能显式地指出函数返回类型,所以难以使用返回类型来区别。因此只剩下了参数来区分,我们可以总结出以下三种常见的函数重载的情况:

参数个数不相同

void func(int a, int b)
{
	cout << a << b << endl;
}
void func(int a)
{
	cout << a << endl;
}
int main()
{
	func(1, 2);
	func(1);
	return 0;
}

参数类型不同

void func(int a, char b)
{
	cout << a << b << endl;
}
void func(int a, int b)
{
	cout << a << b << endl;
}
int main()
{
	func(1, 'a');
	func(1, 2);
	return 0;
}

参数类型顺序不同

void func(int a, char b)
{
	cout << a << b << endl;
}
void func(char b, int a)
{
	cout << b << a << endl;
}
int main()
{
	func(1, 'a');
	func('a', 1);
	return 0;
}

         我们不禁发问,为什么C++支持函数重载,而C语言不支持呢?我以Visual Studio 2022为例,不同编译器的规定方式不同。我们通过观察链接报错,发现C语言中外部符号仅仅是函数名,这就意味着函数在C语言编译中的符号就是函数名,那么相同函数名肯定会产生冲突。

        C++就不一样了,可以发现C++给函数赋予的符号要更加复杂。通过观察规律,我们会发现C++在处理时加入了参数类型(H表示int,D表示char)。因此虽然函数名相同,但是参数不同,编译器依然可以顺利区分,达到函数重载的功能。

5. 引用

5.1 引用简介

        引用是C++相较于C语言创新的地方。引用可以理解为替一个已经存在的变量起一个别名,这就意味着引用一旦定义,那么可以认为引用变量就是原变量,它们是同一个东西。因此定义引用变量时不开辟空间,引用变量和原变量使用同一块空间。

int main()
{
	int a = 1;
	int& ra = a; //定义引用变量
	cout << a << ' ' << ra << endl;
	a++;
	cout << a << ' ' << ra << endl;
	ra++;
	cout << a << ' ' << ra << endl;
	return 0;
}

        定义引用类型的时候,必须和引用实体是同种类型的。

5.2 引用的特性

引用在定义时必须初始化。之所以这么规定与第三条特定有关,因为一旦创建了引用就无法再改变其引用的实体了,所以如果不进行初始化,那就无法再和需要的引用实体进行关联了。即引用变量和引用实体的配对一定发生且只发生在初始化时。

int main()
{
	//int& ra; //error 没有初始化
	int b;
	int& rb = b;
	return 0;
}

一个变量可以存在多个引用

int main()
{
	int a = 1;
	int& ra = a;
	int& rra = a;
	return 0;
}

引用一旦引用一个实体,再不能引用其他实体。这句话就是引用实体不可以发生改变,这是因为会产生歧义。

int main()
{
	int a = 3;
	int b = 8;
	int& ra = a;
	ra = b;//歧义:①将b的值赋值给ra;②将b作为引用变量ra的实体 
	return 0;
}

5.3 常引用

        常引用顾名思义,就是对一个常量的引用,使用const进行修饰限制。值得注意的是,常引用的引用实体并非都是常量,而是一些具有常量性质的值(字面量、常量、常变量、临时变量等)。常引用自从初始化后便不会再改变,换言之不可以通过赋值等手段修改常引用的值(此时的常引用就可以被视为一个常量)。

        需要格外强调的两点:

①定义引用时,权限可以缩小:非常量值作为常引用的引用实体;权限不可以放大:常量值作为非常量引用的引用实体。

②在之前的非常量引用定义时类型不同,不可以初始化的原因是发生了隐式类型转换,使得实际上是将具有常性的临时变量赋给非常量引用,因此错误原因在于权限放大。根据这一点我们就可以知道,常引用尽管类型不同,但是因为隐式类型转换所以依旧可以正常定义。

int main()
{
	const int a = 10;
	//int& ra = a;		//error
	const int& ra = a;	//correct
	
	//int& rb = 10;		//error
	const int& rb = 10;	//correct

	double d = 3.14;
	//int& rd = d;		//error
	const int& rd = d;	//correct,rd==3  d首先隐式类型转换为int,产生一个临时变量,临时变量具有常性所以可以赋给常引用

	d = 4.48;

	cout << d << ' ' << rd << endl; //4.48 3

	return 0;
}

5.4 函数指针的引用

        函数指针的引用注意点在代码注释中给出。

void func(int a)
{
	cout << a << endl;
}
typedef void(**fpp)(int);
typedef void(*fp)(int);
typedef void(f)(int);
int main()
{
	//对于定义函数指针引用:
	//1.注意定义的引用的类型和引用实体的类型相同。
	//	①函数名有两种解释方式,函数名具有右值属性。以func为例:void(*)(int)和void()(int)
	//	②注意辨别不同级函数指针。
	void(*pf)(int) = func;
	void(**ppf)(int) = &pf;
	//2.注意区别左值右值,左值只能使用非常量引用,右值只能使用常量引用
	//		区分左右值的方式:判断能否位于赋值符号左边。如*p可以就是左值,&func不可以就是右值
	//		注意:函数名是右值,所以在定义类似于void()(int)类型的引用(不是指针的函数类型)时,一定是常量引用
	//3.注意定义函数指针的引用可以存在两种方式:①使用typedef;②不使用typedef。
	
	/*f func_;
	f& rfunc_ = func;
	rfunc_ = func;*/ //void()(int)类型的引用一定是常量引用
	
	//非常量引用的定义方式
			//1.使用typedef
	fp& r2 = pf;
		//r2 = func;
	fpp& r3 = ppf;
		//r3 = &r2;
			//2.不使用typedef
	void(*&rr2)(int) = pf;
		//rr2 = func;
	void(**&rr3)(int) = ppf;
		//rr3 = &rr2;
	
	//常量引用的定义方式

		//f& r1 = func;
		//void(&rr1)(int) = func; 
		//以上两种方式虽然可行不报错,但是体现不出常引用,所以不建议使用
		//r1 = func;
		//rr1 = func;
		
			//1.使用typedef
	const f& r1_c = func;
		//r1_c = func;
	const fp& r2_c = pf;
		//r2_c = func;
	const fpp& r3_c = ppf;
		//r3_c = &r2;
			//2.不使用typedef
	void(const &rr1_c)(int) = func;
		//rr1_c = func;
	void(*const &rr2_c)(int) = pf;
		//rr2_c = pf;
	void(**const &rr3_c)(int) = ppf;
		//rr3_c = ppf;

	const fp& r_a = func;
	const fp& r_b = &func;
	void(* const& r_c)(int) = func;
	void(* const& r_d)(int) = &func;
	// 因为函数名有两种解释方式,所以上述的定义方式也是正确的
	
	void(*&r_e)(int) = *ppf;
	//void(*&r_e)(int) = &func;
	//void(*&r_e)(int) = func;
	//因为*ppf是左值,所以没有问题;而&func和func是右值,所以会报错

	return 0;
}

5.5 引用的应用

5.5.1 传递参数(输出型参数)

        C语言中输出型参数采取传指针的方式,C++中可以使用引用来传递,效果相同但是代码更加简洁。

void swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}
int main()
{
	int x = 1;
	int y = 2;
	swap(x, y);
	cout << x << ' ' << y << endl;
	return 0;
}

5.5.2 传递参数(大空间对象传参,减少拷贝)

        在传递如结构体等大空间对象时为了减少拷贝提高效率,可以使用引用来传参。

struct Node
{
	struct Node* next;
	int val;
};
void func(struct Node& node)
{}
int main()
{
	struct Node n1;
	func(n1);
}

5.5.3 做返回值

         这里给出一个引用做返回值的正确例子。我们在下文详细分析引用作返回值的特性。

int& Add(int a,int b)
{
	//static int x = a + b; //这样写因为static只定义一次,所以会导致再次调用函数时不会执行这一步。
	static int x;
	x = a + b;
	return x;
}
int main()
{
	int& a = Add(2,4);
	cout << a << endl;
	a = Add(8, 9);
	cout << a << endl;
	return 0;
}

        那么让引用成为返回值有什么好处与注意事项呢?我们对比着返回值的情况来分析。

        ① 返回引用可以省去拷贝,返回时内存中不产生副本。值返回首先将值交给寄存器,然后释放栈帧,再然后将寄存器的值mov到需要的位置。在此期间就存在拷贝副本的产生,相当于寄存器充当了临时变量的作用。引用返回则是直接返回变量,省去了中间变量的拷贝开销。如果是使用变量接收则相当于拷贝赋值,如果使用引用接收则是相当于定义引用。所以引用返回值效率更高

        ②注意引用返回的变量不可以是局部非静态变量,因为局部非静态变量在栈帧中,当函数结束栈帧释放后,返回值就变得不可控了。

int cal1(int a)
{
	int b = a * 2;
	return b;
}
int& cal2(int a)
{
	static int b;
	b = a * 2;
	return b;
}
int main()
{
	int num1 = cal1(1);		//case1:返回方式:返回值。
							//在栈中开辟空间的b在函数结束后,将值交给临时变量(寄存器),再由临时变量交给num1,内存中产生了返回值的副本(临时变量)
	//int& num2 = cal1(2);	//case2:报错,因为函数返回的值是临时变量,具有常性
	int num3 = cal2(3);		//case3:返回方式:返回引用。
							//在函数结束后,因为是返回引用,所以实际上是返回b变量的别名。而num3的值则是直接从b变量处拷贝而来,避免了副本的产生。
							//因为b变量是一个静态变量,所以不会随着栈帧被释放,才能在栈帧释放后进行拷贝。
	int& num4 = cal2(4);	//case4:返回方式:返回引用。
							//在函数结束后,返回了b变量且不产生副本,而引用声明num4使用返回值初始化,所以num4就成为了b变量的引用、别名。
							//因为b变量是一个静态变量,所以不会随着栈帧被释放,所以引用依旧有效。
}

5.6 引用和指针

①引用相当于是一个别名不占空间。但在底层实现上各个编译器有所不同,一般情况下,引用实际上就是一个指针实现的。所以在实际来说引用还是占空间的。

②在我看来引用是指针的更简单的表达形式。引用变量实际就是指向引用实体的指针,而使用引用变量时默认会进行解引用。

③没有空引用与多级引用但是有空指针和多级指针。

④引用必须初始化并且不可以改变引用实体。指针则可以不受这些限制。

        通过以上叙述发现引用虽然是指针的简练表达,但是仅仅限于某些特定情况下。指针还是通用的方法,所以引用只能是极大简化指针的使用,但是无法代替指针。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前后端项目-part03

文章目录 5.4.4 机构名称5.4.4.1 创建实体类Company5.4.4.2 创建实体类CompanyMapper5.4.4.3 创建实体类CompanyService5.4.4.4 创建实体类CompanyController5.4.4.5 后端测试5.4.4.6 修改basic.js5.4.4.7 修改course.vue5.4.4.8 测试5.4.5 课程标签5.4.5.1 效果5.4.5.2 修改co…

golang学习5,glang的web的restful接口

1. //返回json r.GET("/getJson", controller.GetUserInfo) package mainimport (/*"net/http"*/"gin/src/main/controller""github.com/gin-gonic/gin" )func main() {r : gin.Default()r.GET("/get", func(ctx *…

【Linux系统化学习】信号概念和信号的产生

目录 信号的概念 从生活中的例子中感知信号 前台进程和后台进程 前台进程 后台进程 操作系统如何知道用户向键盘写入数据了&#xff1f; 进程如何得知自己收到了信号&#xff1f; 信号捕捉 signal函数 Core Dump&#xff08;核心转储&#xff09; 信号产生的方式 通…

如何选择合适的汽车芯片ERP系统?

随着汽车产业的飞速发展&#xff0c;汽车芯片作为关键组件&#xff0c;其管理变得愈发重要。为了高效管理汽车芯片的生产、销售、库存等各个环节&#xff0c;许多企业开始引入汽车芯片ERP(企业资源规划)系统。那么&#xff0c;如何选择合适的汽车芯片ERP系统呢? 明确需求是关键…

react-JSX基本使用

1.目标 能够知道什么是JSX 能够使用JSX创建React元素 能够在JSX中使用JS表达式 能够使用JSX的条件渲染和列表渲染 能够给JSX添加样式 2.目录 JSX的基本使用 JSX中使用JS表达式 JSX的条件渲染 JSX的列表渲染 JSX的样式处理 3.JSX的基本使用 3.1 createElement()的问题 A. …

小红书3C家电行业种草营销策略打法,纯干货

小红书作为国内种草营销的鼻祖&#xff0c;拥有庞大的年轻用户群体&#xff0c;特别是在3C家电行业&#xff0c;小红书的种草营销效应更是明显。据相关数据显示&#xff0c;小红书3C家电行业的用户关注度持续攀升&#xff0c;尤其是90后和00后&#xff0c;他们对新鲜事物的接受…

C# 学习第四弹——字符串

一、char类型的使用 字符使用单引号&#xff0c;单个字符 转义字符是一种特殊的字符变量&#xff0c;以反斜线开头&#xff0c;后跟一个或多个字符。 输出多级目录可以使用 二、字符串的声明和初始化 1、引用字符串常量 引用字符串常量初始化——字符使用单引号&#xff0…

基于springboot+vue的学科平台系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

幻兽帕鲁(Palworld 1.4.11.5.0)私有服务器搭建(docker版)

文章目录 说明客户端安装服务器部署1Panel安装和配置docker服务初始化设置设置开机自启动设置镜像加速 游戏服务端部署游戏服务端参数可视化配置 Palworld连接服务器问题总结 服务端升级&#xff08;1.5.0&#xff09; 说明 服务器硬件要求&#xff1a;Linux系统/Window系统&a…

数据中台:数字中国战略关键技术设施

目录 前言 为何要建设数据中台 数据中台建设痛点 数据中台学习资料 聚焦前沿&#xff0c;方法论体系更新 与时俱进&#xff0c;紧跟时代热点 深入6大行业&#xff0c;提炼实践精华 大咖推荐&#xff0c;数字化转型必备案头书 前言 在数字中国这一国家战略的牵引下&…

谷歌seo推广好还是竞价排名好?

事实上seo跟sem竞价并没有任何冲突&#xff0c;也并没有哪个更好的说法&#xff0c;关键在于理解它们各自的优势与局限性&#xff0c;并根据你的业务&#xff0c;预算来配合 Seo推广的优势在于成本&#xff0c;只要你的网站在搜索结果获得高排名&#xff0c;就能有源源不断的点…

水库安全监测方案(福建地区水库安全监测案例分享)

我司星创易联最近在福建省受到了一个水库安全监测系统项目的委托。该水库位于福建中部山区,作为该地区的重要防洪与供水工程,对下游数十万人的生活产生重大影响。但是因为水库附近地质情况复杂,水库大坝在多次洪水冲击下出现一定病害,亟须全面加强对水库大坝安全状况的监测,以确…

Linux零基础快速入门

Linux的诞生 Linux创始人:林纳斯 托瓦兹 Linux 诞生于1991年&#xff0c;作者上大学期间 因为创始人在上大学期间经常需要浏览新闻和处理邮件&#xff0c;发现现有的操作系统不好用,于是他决心自己写一个保护模式下的操作系统&#xff0c;这就是Linux的原型&#xff0c;当时他…

代码随想录算法训练营第35天 | 435. 无重叠区间 ,763.划分字母区间 , 56. 合并区间

贪心算法章节理论基础&#xff1a; https://programmercarl.com/%E8%B4%AA%E5%BF%83%E7%AE%97%E6%B3%95%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html 435. 无重叠区间 题目链接&#xff1a;https://leetcode.cn/problems/non-overlapping-intervals/ 思路&#xff1a; 相信…

【算法与数据结构】复杂度深度解析(超详解)

文章目录 &#x1f4dd;算法效率&#x1f320; 算法的复杂度&#x1f320; 时间复杂度的概念&#x1f309;大O的渐进表示法。 &#x1f320;常见复杂度&#x1f320;常见时间复杂度计算举例&#x1f309;常数阶O(1)&#x1f309;对数阶 O(logN)&#x1f309;线性阶 O(N)&#x…

SpringMVC 学习(十)之异常处理

目录 1 异常处理介绍 2 通过 SimpleMappingExceptionResolver 实现 3 通过接口 HandlerExceptionResolver 实现 4 通过 ExceptionHandler 注解实现&#xff08;推荐&#xff09; 1 异常处理介绍 在 SpringMVC中&#xff0c;异常处理器&#xff08;Exceptio…

九州金榜|父亲在教育中的作用及重要性

随着社会进步&#xff0c;对比以前教育&#xff0c;现在父亲在教育中的作用越来越明显&#xff0c;孩子的教育离不开父亲&#xff0c;父亲在孩子教育中有什么作用&#xff1f;重要性又是什么呢&#xff1f;下面九州金榜家庭教育就带大家一起分析一下作为父亲&#xff0c;在孩子…

项目解决方案:海外门店视频汇聚方案(全球性的连锁店、国外连锁店视频接入和汇聚方案)

目 录 一、概述 二、建设目标及需求 2.1 建设目标 2.2 需求描述 2.3 需求分析 三、建设方案设计 3.1 系统方案拓扑图 3.2 方案描述 3.3 服务器配置推荐 四、产品功能 4.1 资源管理平台 &#xff08;1&#xff09;用户权限管理 &#xff08;2&#xff09…

pv、pvc

目录 1、什么是pv和pvc 2、pvc的使用逻辑 3、StorageClass 4、pv和pvc相互作用 5、pv的生命周期中&#xff0c;一般有几种状态&#xff1f; 6、一个pv从创建到销毁的流程 7、nfs使用pv和pvc 7.1、配置nfs存储 7.2这里定义5个PV&#xff0c;并且定义挂载的路径以及访问…

Tomcat配置私人共享

tomcat安装在公网IP为x.x.x.x的服务器上 tomcat安装 第一步&#xff0c;下载server-jre-8u202-linux-x64.tar.gz和apache-tomcat-8.5.95.tar.gz安装包。 登录地址&#xff1a;https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html下载server-jr…