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;		//errorconst int& ra = a;	//correct//int& rb = 10;		//errorconst int& rb = 10;	//correctdouble d = 3.14;//int& rd = d;		//errorconst int& rd = d;	//correct,rd==3  d首先隐式类型转换为int,产生一个临时变量,临时变量具有常性所以可以赋给常引用d = 4.48;cout << d << ' ' << rd << endl; //4.48 3return 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.使用typedeffp& r2 = pf;//r2 = func;fpp& r3 = ppf;//r3 = &r2;//2.不使用typedefvoid(*&rr2)(int) = pf;//rr2 = func;void(**&rr3)(int) = ppf;//rr3 = &rr2;//常量引用的定义方式//f& r1 = func;//void(&rr1)(int) = func; //以上两种方式虽然可行不报错,但是体现不出常引用,所以不建议使用//r1 = func;//rr1 = func;//1.使用typedefconst f& r1_c = func;//r1_c = func;const fp& r2_c = pf;//r2_c = func;const fpp& r3_c = ppf;//r3_c = &r2;//2.不使用typedefvoid(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.mzph.cn/news/698787.shtml

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

相关文章

Composition API 和 Options API

为什么Composition API 比 Options API 更好 Composition API是Vue.js 3.x版本引入的一种新的组织代码的方式。它相对于Options API有一些明显的优势&#xff0c;使得它在某些场景下更加灵活和易于使用。 更好的逻辑组织&#xff1a;Composition API允许将相关代码逻辑打包在一…

如何进行数据库分区和分片操作?

什么是数据库分区和分片&#xff1f; 数据库分区和分片都是数据库物理设计中的技术&#xff0c;旨在提高数据库的性能和管理大规模数据。 数据库分区是一种物理数据库的设计技术&#xff0c;其主要目的是在特定的SQL操作中减少数据读写的总量以缩减响应时间。分区并不是生成新…

mysql 输出所在月份的最后一天

内置函数 LAST_DAY(date) 参数&#xff1a; date &#xff1a;一个日期或日期时间类型的值&#xff0c;表示要获取所在月份最后一天的日期。 返回值&#xff1a; 返回一个日期值&#xff0c;表示输入日期所在月份的最后一天。 栗子 月总刷题数和日均刷题数_牛客题霸_牛客…

本地配置多个git账户及ll设置

本地配置多个git账户 清除全局配置将命令行&#xff0c;切换到ssh目录生成GitLab和Gitee的公钥、私钥去对应的代码仓库添加 SSH Keys添加私钥ll设置 管理密钥验证仓库配置关于gitgitee.com: Permission denied (publickey) 清除全局配置 此步骤可以不做&#xff0c;经测试不影…

总结一下最近几个主界面

目前展示了用Avalonia做几个主要流行的主界面&#xff0c;演示了一下组件的使用。用不同的实现方式实现一些方法。 1、独立大屏展示&#xff0c;类似一个实时监控&#xff0c;这是一种目前很方便的大屏效果。 主要涉及的内内容&#xff1a; &#xff08;1&#xff09;窗标题实…

【视频编码\VVC】环路滤波基础知识

本文为新一代通用视频编码H.266\VVC原理、标准与实现的简化笔记。 定义&#xff1a;在视频编码过程中进行滤波&#xff0c;滤波后的图像用于后续编码。 目的&#xff1a;1、提升编码图像的质量。2、为后续编码图像提供高质量参考&#xff0c;获得更好的预测效果。 VVC中主要…

使用LinkedList实现堆栈及Set集合特点、遍历方式、常见实现类

目录 一、使用LinkedList实现堆栈 堆栈 LinkedList实现堆栈 二、集合框架 三、Set集合 1.特点 2.遍历方式 3.常见实现类 HashSet LinkedHashSet TreeSet 一、使用LinkedList实现堆栈 堆栈 堆栈&#xff08;stack&#xff09;是一种常见的数据结构&#xff0c;一端…

后端程序员入门react笔记(五)ajax请求

常见的ajax Ajax 最原始的方式&#xff0c;基于原生的jsXmlHttpRequest 多个请求之间如果有先后关系&#xff0c;会存在很多层回调的问题&#xff0c;也是基于原生jsJquery Ajax 基于原生XHR封装&#xff0c;依赖Jquery框架&#xff0c;由jquery 框架去封装原生的XML(Xml)封装…

【高德地图】Android高德地图控件交互详细介绍

&#x1f4d6;第5章 与地图控件交互 ✅控件交互&#x1f9ca;缩放按钮&#x1f9ca;指南针&#x1f9ca;定位按钮&#x1f9ca;地图Logo ✅手势交互&#x1f9ca;缩放手势&#x1f9ca;滑动手势&#x1f9ca;旋转手势&#x1f9ca;倾斜手势&#x1f9ca;指定屏幕中心点的手势操…

CSP-J 2023 T1 小苹果

文章目录 题目题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示 题目传送门题解思路总代码 提交结果尾声 题目 题目描述 小 Y 的桌子上放着 n n n 个苹果从左到右排成一列&#xff0c;编号为从 1 1 1 到 n n n。 小苞是小 Y 的好朋友&#xff0c;每天她都会从…

EXCEL 在列不同单元格之间插入N个空行

1、第一步数据&#xff0c;要求在每个数字之间之间插入3个空格 2、拿数据个数*&#xff08;要插入空格数1&#xff09; 19*4 3、填充 4、复制数据到D列 5、下拉数据&#xff0c;选择复制填充这样1-19就会重复4次 6、全选数据D列排序&#xff0c;这样即完成了插入空格 以…

MySQL问题记录

问题 Ubuntu2204 通过 apt 安装 mysql-server8.0.36 后&#xff0c;数次修改密码不生效&#xff0c;仍可无密码登录。 解决 mysql_native_password 是MySQL 5.7及之前版本使用的默认身份验证插件。在MySQL 8.0及更高版本中&#xff0c;默认的身份验证插件 caching_sha2_pass…

密评技术要求实施详解:每一步都关键

密评简介 密评定义&#xff1a;全称商用密码应用安全性评估, 是对采用商用密码技术、产品和服务集成建设的网络和信息系统密码应用的合规性、正确性、有效性进行评估的活动。 评测依据&#xff1a;GB/T 39786-2021《信息安全技术 信息系统密码应用基本要求》。 密评对象&…

新能源汽车PACK电池包的气密性测试需要用到哪些快速密封连接器

PACK电池包是新能源汽车的重要部件之一&#xff0c;在全部组装完成后需要对其壳体进行气密性测试&#xff0c;以确保壳体的密封性能&#xff0c;避免有雨水、灰尘等外界侵扰拒之门外&#xff0c;从而保证电池的使用寿命不受损害。 新能源汽车PACK电池包 在做气密性测试时需要用…

VoIP(Voice over Internet Protocol 基于IP的语音传输)介绍(网络电话、ip电话)

文章目录 VoIP&#xff08;基于IP的语音传输&#xff09;1. 引言2. VoIP基础2.1 VoIP工作原理2.2 VoIP协议 3. VoIP的优势和挑战3.1 优势3.2 挑战 4. VoIP的应用5. 总结 VoIP&#xff08;基于IP的语音传输&#xff09; 1. 引言 VoIP&#xff0c;全称Voice over Internet Prot…

更多闰年数

输入2 个正整数a和b&#xff0c;表示开始的年份和结束的年份&#xff0c;问从a年到b年有多少闰年&#xff1f; 输入格式 第一行2个整数a和b&#xff0c;范围在[1, 1,000,000,000]。 输出格式 只一个整数。 输入/输出例子1 输入&#xff1a; 1 10000 输出&#xff1a; …

Jmeter基础(3) 发起一次请求

目录 Jmeter 一次请求添加线程组添加HTTP请求添加监听器 Jmeter 一次请求 用Jmeter进行一次请求的过程&#xff0c;需要几个步骤呢&#xff1f; 1、添加线程组2、添加HTTP请求3、添加监听器&#xff0c;查看结果树 现在就打开jmeter看下如何创建一个请求吧 添加线程组 用来…

致CentOS 7普通用户之装机初始化说明

一&#xff1a;CentOS 7安装盘制作 1.准备&#xff1a;linux操作系统安装包&#xff0c;百度搜索下载&#xff1a;CentOS 7。&#xff08;一般安装包&#xff0c;包含CentOS 7全系列版本&#xff0c;自己选择安装配置&#xff09; 2.准备&#xff1a;rufus.exe安装盘制作软件…

CUDA自学笔记001 CUDA编程模型、CUDA线程模型及其管理、CUDA内存模型及其管理

CUDA编程模型 我们使用CUDA_C语言进行CUDA编程&#xff0c; 1&#xff0c;CUDA编程模型提供了线程抽象接口用于控制GPU中的线程 2&#xff0c;CUDA编程模型提供了内存访问控制&#xff0c;我们可以实现主机和GPU设备内存的控制&#xff0c;我们可以实现CPU和GPU之间内存的数据传…

yolov5-tracking-xxxsort yolov5融合六种跟踪算法(三)--目标跟踪

本次开源计划主要针对大学生无人机相关竞赛的视觉算法开发。 开源代码仓库链接&#xff1a;https://github.com/zzhmx/yolov5-tracking-xxxsort.git 先按照之前的博客配置好环境&#xff1a; yolov5-tracking-xxxsort yolov5融合六种跟踪算法&#xff08;一&#xff09;–环境配…