0


【C++】引用

开始之前,值得说的就是:对于引用,实际上,存在着非常多的细节,我们要有耐心去理解。

文章目录

1.引用概念

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

对于引用的概念还是比较好理解的。引用的基本用法:

类型& 引用变量名(对象名) = 引用实体

我们不妨来举个例子:

image-20220916153934855

如何去应用我们的引用呢?我们以从开始就接触到的Swap()函数为例子,之前我们都是用指针去进行实现,现在我们可以用引用来实现:

image-20220916154653072

交换的时候更加的方便,不需要进行解引用。

另外,有了引用,我们就可以对之前链表实现用到的二级指针进行改写了,使之看起来更加简便,我们只需要typedef一个指针,在对其进行引用即可:

image-20220916160511882

我们可以发现,引用可以简化一些代码的实现,对于刚开始引用的概念我们就先说到这里,接着往后面走下去👇


2.引用特性

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

我们来看第一个引用的特性,引用在定义时必须要初始化,如果没有初始化,我们来演示一下:

image-20220916162204163

引用一旦引用一个实体,再不能引用其他实体 ,只是改变值而已,我们可以来举个例子,调试起来看一看:

image-20220916163654334

我们可以看到,rra的地址还是a的地址,只是值变成了b值而已。

对于引用的特性而言,我们还是能够很好去理解的。


对于C++指针和引用而言,是相辅相成的。

下面,我们来看看引用的使用场景

3.使用场景

  • 做参数

image-20220916172500210

  • 做返回值

image-20220916192815369

那如果没有static进行修饰呢?会出现什么问题❓

有static是把变量放在了静态区,出了函数不会销毁。而没有static的时候,空间销毁了

空间销毁意味着什么

首先,空间销毁后空间还是在的,只是使用权不是我们的了,我们存的数据不再被保护。

我们可以去进行访问,只是读写的数据是不确定的。

image-20220916195302653

我们在来看另一个代码:

image-20220916202828888

我们可以发现,在第三行中出现了随机值。为什么会出现❓

第三行出现随机值的原因是因为cout也是一个函数,会进行函数调用。因为第一次cout调用取参数ret没被改还是1,后面cout调用的时候被覆盖了,所以成为了随机值。第三次调用Func(),使之成为了100。

实际上,我们上面都是在讨论一个不太合适的程序,函数返回值是引用,语法上没有报错,但是运行结果却是不确定的。

所以,在这里,我们需要注意到一个点

注意:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回

也就是说,出了函数作用域,返回变量不存在了,不能用引用返回,因为引用返回的结果是未定义的。如果出了函数作用域,返回变量存在,才能用引用返回。

所以,下面才是正确的(这里的差别在于static的修饰):

image-20220917144019911

到了这里,问题就来了,对于值、引用作为返回值类型究竟有什么区别呢❓

该怎么去选择呢👇


4.值和引用作为返回值类型

对于传引用做返回值类型:

  • 减少拷贝,提高效率
  • 修改返回值
  • 引用做参数

我们可以来测试一下效率究竟是如何的:

image-20220916225248690

image-20220916225303737

我们可以明显看到,引用的效率确实是提高了。

除此之外,引用也可以作为参数,也可以修改返回值。


5.传值、传引用做参数

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变

量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就

更低

而对于引用做参数而言:

  • 减少拷贝,提高效率
  • 输出型参数,改变形参,实参也改变了

下面我们来测试一下第一点效率有什么区别:

image-20220917083138254

image-20220917083154921

通过上述代码的比较,发现传值和引用在作为传参以及返回值类型上效率相差很大

我们在来看看第二个点:输出型参数,改变形参,实参也改变了

image-20220917083815910

看到这里,问题又又又来了:这两个Swap构不构成重载

下面,我们来试一试就知道了。实际上,不管构不构成,调用时都会产生歧义

image-20220917084705690


6.常引用

image-20220917124906913

这里用const修饰的变量,不能直接去进行引用,在指针和引用赋值中,权限可以缩小,但是不能放大。这里的权限缩小指的是原来可读可写后变成只是可读。

image-20220917125719026

关于常引用的用处:

一般用引用做参数时都是用const修饰引用。如果没有加const修饰会出现什么情况,我们不妨来看一看:

image-20220917130603603

如果没有const修饰引用做参数,可能会遇到一些有const修饰的变量做参数传过去,导致权限放大了,会报错。加上const修饰就不会了(使权限缩小或者不变):

image-20220917130806048

这是const修饰引用的优点所在。当然,如果需要对传过来的参数需要进行修改的话,那就不需要用const修饰。而本身变量带有const修饰的话,我们根本就不需要取进行修改。

好的,到了这里,我们对于常引用有了一定的了解,接着往下走👇

除此之外,我们知道,在类型转化的时候会产生一个临时变量,而这个临时变量具有常性,所以引用的时候一定要记得加上const,我们来举个例子你就明白什么意思了

image-20220917133344746

对于函数的调用也是同理,我们来看一看:

image-20220917134453520

返回的是n的临时拷贝,具有常性,我们可以用const修饰来接收即可。

image-20220917134740253

至此,我们解决了大部分的问题 。


7.引用和指针的区别

语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间

<>

底层实现上实际是有空间的,因为引用是按照指针方式来实现

怎么去理解这句话,我们可以去扒拉一下引用和指针的反汇编代码对比(ra是指针变量)

image-20220917140832860

我们可以看到的是:引用是按照指针方式来实现

到这里,通过上面对引用的介绍,我们可以与指针做一个对比。

引用和指针的不同点 :

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

总结

到了这里,对于引用的基本知识我们算是具体说完了。说多不多,说少也不少,其中的知识还是值得我们去学习的,这次就先到这里结束了🌹


本文转载自: https://blog.csdn.net/weixin_60478154/article/details/126905610
版权归原作者 平凡的人1 所有, 如有侵权,请联系我们删除。

“【C++】引用”的评论:

还没有评论