C++:引用的简单理解

news2024/11/13 16:18:25

前言:引用是C++一个很重要的特性,最近看了很多有关引用的资料和博客,故在此对引用的相关知识进行总结

一、什么是引用

引用,顾名思义是某一个变量或对象的别名,对引用的操作与对其所绑定的变量或对象的操作完全等价

语法:类型 &引用名=目标变量名;

特别注意:

1.&不是求地址运算符,而是起标志作用

2.引用的类型必须和其所绑定的变量的类型相同

1 #include<iostream>
2 using namespace std;
3 int main(){
4     double a=10.3;
5     int &b=a; //错误,引用的类型必须和其所绑定的变量的类型相同
6     cout<<b<<endl;
7 }

为了帮助您更好地入门并深入掌握C++,我们精心准备了一系列丰富的学习资源包,包括但不限于基础语法教程、实战项目案例、核心概念解析以及进阶技巧指导等。

您只扫码上方二维码,即可免费获取这份专属的学习礼包。我们的教程覆盖了C++语言的各个方面,旨在让您在理论学习与实践操作中不断进步,提升编程技能。

同时,我们也鼓励您在学习过程中遇到任何问题时积极提问,我们会尽全力提供解答和帮助。期待您在C++编程的道路上越走越远,早日成为一位优秀的C++开发

3.声明引用的同时必须对其初始化,否则系统会报错

1 #include<iostream>
2 using namespace std;
3 int main(){
4     int &a; //错误!声明引用的同时必须对其初始化
5     return 0;
6 }

4.引用相当于变量或对象的别名,因此不能再将已有的引用名作为其他变量或对象的名字或别名

5.引用不是定义一个新的变量或对象,因此内存不会为引用开辟新的空间存储这个引用

1 #include<iostream>
2 using namespace std;
3 int main(){
4     int value=10;
5     int &new_value=value;
6     cout<<"value在内存中的地址为:"<<&value<<endl;
7     cout<<"new_value在内存中的地址为:"<<&new_value<<endl;
8     return 0;
9 }

 

6.对数组的引用

语法:类型 (&引用名)[数组中元素数量]=数组名;
 1 #include<iostream>
 2 using namespace std;
 3 int main(){
 4     int a[3]={1,2,3};
 5     int (&b)[3]=a;//对数组的引用 
 6     cout<<&a[0]<<" "<<&b[0]<<endl;
 7     cout<<&a[1]<<" "<<&b[1]<<endl;
 8     cout<<&a[2]<<" "<<&b[2]<<endl;
 9     return 0;
10 }

 

7.对指针的引用

语法:类型 *&引用名=指针名;//可以理解为:(类型*) &引用名=指针名,即将指针的类型当成类型*
1 #include<iostream>
2 using namespace std;
3 int main(){
4     int a=10;
5     int *ptr=&a;
6     int *&new_ptr=ptr;
7     cout<<&ptr<<" "<<&new_ptr<<endl;
8     return 0; 
9 }

 

二、引用的应用

A.引用作为函数的参数

 1 #include<iostream>
 2 using namespace std;
 3 void swap(int &a,int &b){//引用作为函数的参数
 4     int temp=a;
 5     a=b;
 6     b=temp; 
 7 }
 8 int main(){
 9     int value1=10,value2=20;
10     cout<<"----------------------交换前----------------------------"<<endl;
11     cout<<"value1的值为:"<<value1<<endl; 
12     cout<<"value2的值为:"<<value2<<endl;
13     swap(value1,value2); 
14     cout<<"----------------------交换后----------------------------"<<endl;
15     cout<<"value1的值为:"<<value1<<endl; 
16     cout<<"value2的值为:"<<value2<<endl;
17     return 0;
18 }

 

特别注意:

1.当用引用作为函数的参数时,其效果和用指针作为函数参数的效果相当。当调用函数时,函数中的形参就会被当成实参变量或对象的一个别名来使用,也就是说此时函数中对形参的各种操作实际上是对实参本身进行操作,而非简单的将实参变量或对象的值拷贝给形参

2.通常函数调用时,系统采用值传递的方式将实参变量的值传递给函数的形参变量。此时,系统会在内存中开辟空间用来存储形参变量,并将实参变量的值拷贝给形参变量,也就是说形参变量只是实参变量的副本而已;并且如果函数传递的是类的对象,系统还会调用类中的拷贝构造函数来构造形参对象。而使用引用作为函数的形参时,由于此时形参只是要传递给函数的实参变量或对象的别名而非副本,故系统不会耗费时间来在内存中开辟空间来存储形参。因此如果参数传递的数据较大时,建议使用引用作为函数的形参,这样会提高函数的时间效率,并节省内存空间

3.使用指针作为函数的形参虽然达到的效果和使用引用一样,但当调用函数时仍需要为形参指针变量在内存中分配空间,而引用则不需要这样,故在C++中推荐使用引用而非指针作为函数的参数

4.如果在编程过程中既希望通过让引用作为函数的参数来提高函数的编程效率,又希望保护传递的参数使其在函数中不被改变,则此时应当使用对常量的引用作为函数的参数。

5.数组的引用作为函数的参数:C++的数组类型是带有长度信息的,引用传递时如果指明的是数组则必须指定数组的长度

 

 1 #include<iostream>
 2 using namespace std;
 3 void func(int(&a)[5]){//数组引用作为函数的参数,必须指明数组的长度 
 4 //函数体 
 5 }
 6 int main(){
 7     int number[5]={0,1,2,3,4};
 8     func(number); 
 9     return 0; 
10  }

B.常引用

语法:const 类型 &引用名=目标变量名;

常引用不允许通过该引用对其所绑定的变量或对象进行修改        

1 #include<iostream>
2 using namespace std;
3 int main(){
4     int a=10;
5     const int &new_a=a;
6     new_a=11;//错误!不允许通过常引用对其所绑定的变量或对象进行修改 
7     return 0;
8 }

特别注意:

先看下面的例子

 1 #include<iostream>
 2 #include<string> 
 3 using namespace std;
 4 string func1(){
 5     string temp="This is func1";
 6     return temp;
 7 }
 8 void func2(string &str){
 9     cout<<str<<endl;
10 }
11 int main(){
12     func2(func1());
13     func2("Tomwenxing");
14     return 0;
15 }

运行上面的程序编译器会报错

这是由于func1()和“Tomwenxing”都会在系统中产生一个临时对象(string对象)来存储它们,而在C++中所有的临时对象都是const类型的,而上面的程序试图将const对象赋值给非const对象,这必然会使程序报错。如果在函数func2的参数前添加const,则程序便可正常运行了

 1 #include<iostream>
 2 #include<string> 
 3 using namespace std;
 4 string func1(){
 5     string temp="This is func1";
 6     return temp;
 7 }
 8 void func2(const string &str){
 9     cout<<str<<endl;
10 }
11 int main(){
12     func2(func1());
13     func2("Tomwenxing");
14     return 0;
15 }

C.引用作为函数的返回值

语法:类型 &函数名(形参列表){ 函数体 }

特别注意:

1.引用作为函数的返回值时,必须在定义函数时在函数名前将&

2.用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本

 1 //代码来源:RUNOOB
 2 #include<iostream>
 3 using namespace std;
 4 float temp;
 5 float fn1(float r){
 6     temp=r*r*3.14;
 7     return temp;
 8 } 
 9 float &fn2(float r){ //&说明返回的是temp的引用,换句话说就是返回temp本身
10     temp=r*r*3.14;
11     return temp;
12 }
13 int main(){
14     float a=fn1(5.0); //case 1:返回值
15     //float &b=fn1(5.0); //case 2:用函数的返回值作为引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
                           //(有些编译器可以成功编译该语句,但会给出一个warning) 
16     float c=fn2(5.0);//case 3:返回引用
17     float &d=fn2(5.0);//case 4:用函数返回的引用作为新引用的初始化值
18     cout<<a<<endl;//78.5
19     //cout<<b<<endl;//78.5
20     cout<<c<<endl;//78.5
21     cout<<d<<endl;//78.5
22     return 0;
23 }

 

 上例中4个case的说明解释:

case 1:用返回值方式调用函数(如下图,图片来源:伯乐在线):

返回全局变量temp的值时,C++会在内存中创建临时变量并将temp的值拷贝给该临时变量。当返回到主函数main后,赋值语句a=fn1(5.0)会把临时变量的值再拷贝给变量a

case 2:用函数的返回值初始化引用的方式调用函数(如下图,图片来源:伯乐在线)

这种情况下,函数fn1()是以值方式返回到,返回时,首先拷贝temp的值给临时变量。返回到主函数后,用临时变量来初始化引用变量b,使得b成为该临时变量到的别名。由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句float &b=fn1(5.0);之后) ,所以b面临无效的危险,很有可能以后的值是个无法确定的值。

 如果真的希望用函数的返回值来初始化一个引用,应当先创建一个变量,将函数的返回值赋给这个变量,然后再用该变量来初始化引用:

1 int x=fn1(5.0);
2 int &b=x;

case 3:用返回引用的方式调用函数(如下图,图片来源:伯乐在线)

这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数,即主函数的赋值语句中的左值是直接从变量temp中拷贝而来(也就是说c只是变量temp的一个拷贝而非别名) ,这样就避免了临时变量的产生。尤其当变量temp是一个用户自定义的类的对象时,这样还避免了调用类中的拷贝构造函数在内存中创建临时对象的过程,提高了程序的时间和空间的使用效率。

case 4:用函数返回的引用作为新引用的初始化值的方式来调用函数(如下图,图片来源:伯乐在线)

这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数。在主函数中,一个引用声明d用该返回值初始化,也就是说此时d成为变量temp的别名。由于temp是全局变量,所以在d的有效期内temp始终保持有效,故这种做法是安全的。

3.不能返回局部变量的引用。如上面的例子,如果temp是局部变量,那么它会在函数返回后被销毁,此时对temp的引用就会成为“无所指”的引用,程序会进入未知状态。

4.不能返回函数内部通过new分配的内存的引用。虽然不存在局部变量的被动销毁问题,但如果被返回的函数的引用只是作为一个临时变量出现,而没有将其赋值给一个实际的变量,那么就可能造成这个引用所指向的空间(有new分配)无法释放的情况(由于没有具体的变量名,故无法用delete手动释放该内存),从而造成内存泄漏。因此应当避免这种情况的发生

5当返回类成员的引用时,最好是const引用。这样可以避免在无意的情况下破坏该类的成员。

6.可以用函数返回的引用作为赋值表达式中的左值

 1 #include<iostream>
 2 using namespace std;
 3 int value[10];
 4 int error=-1;
 5 int &func(int n){
 6     if(n>=0&&n<=9)
 7         return value[n];//返回的引用所绑定的变量一定是全局变量,不能是函数中定义的局部变量 
 8     else
 9         return error;
10 }
11 
12 int main(){
13     func(0)=10;
14     func(4)=12;
15     cout<<value[0]<<endl;
16     cout<<value[4]<<endl;
17     return 0; 
18 }

D.用引用实现多态

在C++中,引用是除了指针外另一个可以产生多态效果的手段。也就是说一个基类的引用可以用来绑定其派生类的实例

class Father;//基类(父类)
class Son:public Father{.....}//Son是Father的派生类
Son son;//son是类Son的一个实例
Father &ptr=son;//用派生类的对象初始化基类对象的使用

特别注意:

ptr只能用来访问派生类对象中从基类继承下来的成员如果基类(类Father)中定义的有虚函数,那么就可以通过在派生类(类Son)中重写这个虚函数来实现类的多态。

 

三、总结

1.在引用的使用中,单纯给某个变量去别名是毫无意义的,引用的目的主要用于在函数参数的传递中,解决大块数据或对象的传递效率和空间不如意的问题

2.用引用传递函数的参数,能保证参数在传递的过程中不产生副本,从而提高传递效率,同时通过const的使用,还可以保证参数在传递过程中的安全性

3.引用本身是目标变量或对象的别名,对引用的操作本质上就是对目标变量或对象的操作。因此能使用引用时尽量使用引用而非指针

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

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

相关文章

MATLAB:优化与规划问题

一、线性规划 % 线性规划&#xff08;Linear programming, 简称LP&#xff09; fcoff -[75 120 90 105]; % 目标函数系数向量 A [9 4 7 54 5 6 105 10 8 53 8 9 77 6 4 8]; % 约束不等式系数矩阵 b [3600 2900 3000 2800 2200]; % 约束不等式右端向量 Aeq []; % 约束等式系…

短剧直播项目,一个信息赚3600+,小白也能轻松上手!

一位陌生朋友的一句话&#xff0c;我让他赚了3600&#xff0c;你敢相信吗&#xff1f; 事情是在上个礼拜六&#xff0c;一位朋友通过知乎添加我&#xff0c;问我短剧怎么授权直播。我说现在只能授权剪辑&#xff0c;没有授权直播的权限。于是他又问我&#xff0c;你们有教短剧…

Excel·VBA数组平均分组问题

看到一个帖子《excel吧-数据分组问题》&#xff0c;对一组数据分成4组&#xff0c;使每组的和值相近 上一篇文章《ExcelVBA数组分组问题》&#xff0c;解决了这个帖子问题的第1步&#xff0c;即获取所有数组分组形式的问题 接下来要获取分组和值最相近的一组&#xff0c;只需计…

2024年洗地机综合实力排行榜:谁才是真正的洗地神器?

近年来&#xff0c;洗地机在行业里&#xff0c;它集合了扫地和拖地以及自动清洁和除菌的功能&#xff0c;备受人们的喜爱&#xff0c;尤其是平时忙于工作并没有多少时间清洁家务的用户&#xff0c;但是对于第一次接触洗地机的用户来说&#xff0c;怎么选购洗地机也是个问题&…

2024新版本知识付费微信小程序源码_亲测可用(附下载链接)

介绍&#xff1a; 主要功能 会员系统&#xff0c;用户登录/注册购买记录 收藏记录 基本设置 后台控制导航颜色 字体颜色 标题等设置 流量主广告开关小程序广告显示隐藏 广告主审核过审核 资源管理 后台可以添加5种类型资源灵活设置 激励广告解锁资源 vip专享资源 免费资源…

代码随想录算法训练营第三十四天 | 1005.K次取反后最大化的数组和,134. 加油站 , 135. 分发糖果

贪心&#xff0c;每次选择最小的数来取反 用了一个优先级队列来做处理&#xff0c;维护一个顺序的数组 class Solution { public:typedef struct comp{bool operator()(const int & a,const int & b){return a > b;}}comp;int largestSumAfterKNegations(vector<…

JavaEE初阶——关于进程

此篇文章和大家一起分享关于操作系统对进程的调度 目录 1.操作系统 操作系统的构成 2.进程 2.1进程的概念 2.2管理 2.3pcb (1)pid (2)内存指针 ​编辑 (3)文件描述符 (4)进程调度信息 并行执行 并发执行 pcb中支持进程调度的属性 谢谢您的访问!!期待您的关注!! …

【概念验证(POC):技术项目开发的关键一步】

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老导航 檀越剑指大厂系列:全面总结 jav…

redis开启aof持久化失败,无法生成.aof文件

大概率是因为redis-server服务没有关&#xff0c;关掉之后先删除.rdb文件&#xff0c;然后重启server&#xff0c;再去使用cli&#xff0c;然后退出应该就有.aof文件了 1、改配置文件 在redis.conf文件中搜索appendonly&#xff0c;然后将no修改为yes&#xff0c;保存并退出。…

程序员必会技能—股权之舟引领创业者驶向成功之岸【文末送书-44】

文章目录 程序员必会技能—股权之舟引领创业者驶向成功之岸股权进阶&#xff1a;让股权助创业成功的核心3问【文末送书-44】 程序员必会技能—股权之舟引领创业者驶向成功之岸 在创业过程中&#xff0c;股权管理是至关重要的一环。正确处理股权关系可以促进公司的发展&#xf…

RuoYi-Vue-Plus(登录流程-验证码生成)

一、登录流程 1- 进入登录页面&#xff0c;调用 com.ruoyi.web.controller.common.CaptchaController 类中的 captchaImage 方法&#xff0c;生成base64的图片 以及 UUID 2- 提交 登录信息 验证码 uuid 比对 错误&#xff1a;返回错误信息&#xff0c;删除缓存的验证码 成功…

vivado Modifying Logic

修改逻辑 在中实现后&#xff0c;可以修改非只读逻辑对象的属性Vivado IDE以及Tcl。 注意&#xff1a;有关Tcl命令的更多信息&#xff0c;请参阅Vivado Design Suite Tcl命令参考指南&#xff08;UG835&#xff09;&#xff0c;或键入&#xff1c;command&#xff1e;-help。…

SEO 谷歌浏览器模拟baodu蜘蛛 模拟UA 设置UA

目录 前言baidu UA操作设置百度UA 前言 要在谷歌浏览器中设置用户代理&#xff08;User Agent&#xff09;来模拟百度蜘蛛&#xff0c;您可以按照以下步骤进行操作 baidu UA Mozilla/5.0 (compatible; Baiduspider-render/2.0; http://www.baidu.com/search/spider.html)操作…

Linux(1)常用指令总结大全

1、firewall-cmd 1.1 打开443/TCP端口 firewall-cmd --add-port443/tcp 1.2 永久打开3690/TCP端口 firewall-cmd --permanent --add-port3690/tcp ​ # 永久打开端口好像需要reload一下&#xff0c;临时打开好像不用&#xff0c;如果用了reload临时打开的端口就失效了 # …

【无标题】C高级325

练习1&#xff1a;输入一个数&#xff0c;实现倒叙123-》321 练习2&#xff1a;输入一个&#xff0c;判断是否是素数 练习3&#xff1a;输入一个文件名&#xff0c; 判断是否在家目录下存在, 如果是一个目录&#xff0c;则直接输出是目录下的sh文件的个数 如果存在则判断是否是…

java常用应用程序编程接口(API)——IO流概述及字节流的使用

前言&#xff1a; IO流和File是用于把数据存放在硬盘里的工具。File可以把文件存至硬盘&#xff0c;但不能更改里面的数据。通过IO流可以改写硬盘里的数据。整理下学习笔记&#xff0c;打好基础&#xff0c;daydayup!!! IO流 I指Input&#xff0c;称为输入流&#xff1a;负责把…

【好书推荐2】AI提示工程实战:从零开始利用提示工程学习应用大语言模型

【好书推荐2】AI提示工程实战&#xff1a;从零开始利用提示工程学习应用大语言模型 写在最前面AI辅助研发方向一&#xff1a;AI辅助研发的技术进展方向二&#xff1a;行业应用案例方向三&#xff1a;面临的挑战与机遇方向四&#xff1a;未来趋势预测方向五&#xff1a;与法规的…

扬州大数据局领导一行参访百望云 探索新质生产力在百态千业的落地场景

​ 在当前全球经济一体化的大背景下&#xff0c;新质生产力的发展代表着科技进步、产业升级和商业模式创新的方向。特别两会期间&#xff0c;新质生产力概念爆火&#xff0c;也体现出我国政府高度重视新质生产力&#xff0c;将其作为实现高质量发展的重要支柱。 在此背景下&…

Day20 代码随想录(1刷) 二叉树

目录 654. 最大二叉树 617. 合并二叉树 700. 二叉搜索树中的搜索 98. 验证二叉搜索树 654. 最大二叉树 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数…

今日arXiv最热NLP大模型论文:微软重磅:AgentAI,下一代人工智能的关键

人工智能的发展已从简单算法进化到复杂的大型基础模型&#xff0c;尤其在理解开放世界环境中的感官信息方面取得显著进步。然而&#xff0c;关键转折点在于从过度简化方法转向强调整体运作的系统&#xff0c;催生了Agent AI的兴起。Agent AI将大型基础模型整合到代理行动中的具…