室友一把王者的时间,我熟练掌握了C++之引用

1.引用的概念

C++很珍惜关键字和操作符,对于引用C++没有定义新的关键字和操作符,而是共用了C语言的&(取地址操作符)。在C++中把&放在类型的后面,变量的前面就叫作引用。
类型& 引用变量名(对象名) = 引用实体;

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

注意:引用类型必须和引用实体是同种类型的。

2.引用的特性

2.1. 引用在定义时必须初始化

在这里插入图片描述

2.2. 一个变量可以有多个引用

我们说b是a的引用,也可以说b是a的别名。一个变量可以有多个别名,甚至可以对一个变量的别名取别名,但本质上是同一块内存空间,对任何一个引用进行修改,其他的引用也被修改,因为本质上是同一块内存空间的内容被修改了。
在这里插入图片描述

2.3. 引用一旦引用一个实体,再不能引用其他实体

大家想想,假设我现在又定义了一个变量e,e的值是20。那我把e给b在这个地方到底是什么意思呢?是把e的值赋值给b这块空间呢,还是说让b变成e的别名呢?
很显然,这里必然是把e的值赋值给b。
在这里插入图片描述

3.常引用

3.1.引用的权限问题

以前我们学习指针时,除了学习普通指针外,还有一个const指针。那么引用涉不涉及这个const引用呢?其实也涉及而且在很多场景都有着重要的作用。
假设有人定义了一个变量x,这个x是const int的,那么我现在给x取别名我怎么取呢?像之前讲解的方法一样取别名可以吗?ok,请看下图:
在这里插入图片描述
我们发现按照之前的方法没办法去引用x,那怎么样才能引用呢?其实我们只要在引用时加一个const就行了。那我们能不能用加了const的引用来引用普通的没加const的int变量呢?
在这里插入图片描述
诶,我们发现这样也是可以的,为什么这里可以这样引用呢?那引用之间的关系到底是什么呢?
记住,取别名的原则:对引用实体变量,引用的权限(主要指读写权限)只能缩小,不能放大。
在这里插入图片描述
为什么说30行int& y = x;是引用权限放大呢?因为变量x是const的,要求只能被读,不能被修改,而引用y是int型,既能读又能修改,所以引用权限被放大,这种写法是不可行的(有点喧宾夺主的意思),而权限缩小是可以的。这里和指针还有点不一样,因为指针既可以const指针变量本身,也可以const指针变量所指向的内容。

3.2.引用可以引用常量

那我们给一个常量取别名可不可以呢?我们发现给常量取别名也要加上const。
在这里插入图片描述

3.3.在类型转换中为什么可以用引用

**ok,重头戏来了。**我们知道引用类型必须和引用实体是同种类型的。像double d = 2.2;int& e = d;这种写法肯定是不行的,那为什么引用时加一个const就可以了呢?如图:

在这里插入图片描述
我们发现只给了一个警告,没有报错。这是为什么呢?
我们知道C++是基于C语言发展而来的,C++就要沿袭C语言的一些特性。我们知道C语言大一点的类型赋值给小一点的类型会发生截断,反之则会提升。这个我们以前也讲过。
在这里插入图片描述
这里int f=d;严格来说不是截断,因为浮点数的存储规则和整数不同,这里实际上是发生转换,把浮点数部分的数据丢掉。
但是大家要在另外一个方面理解一个过程:赋值不是直接赋值的,赋值过程会借助一个临时变量来完成。
因为d是8个字节,它无法直接赋值给f,而且d是浮点数,存储规则不一样,截断也不好直接截。从原理上来说,编译器会把d的整数部分取出来,赋值给一个临时变量(大小是4个字节),临时变量再赋值给f。也就是说隐式类型转换也会产生一个临时变量,临时变量具有常性(就像它被const修饰了一样,不能被修改)。
在这里插入图片描述

所以之前double d = 2.2; int& e = d;e不能引用d并不是因为类型不同,而是因为e引用的是中间产生的临时变量,而临时变量具有常性,临时变量赋值给e是不是权限就放大了呀!加上const就是为了让权限不发生改变。
那有可爱的小伙伴就要问了:那为什么int f = d;不用加const呢?
因为e其实是临时变量的别名,如果不加const是不是意味着要改变临时变量,但临时变量不可改。而int f = d;是赋值,是把临时变量的值拷贝给f,f的改变不会影响临时变量。普通对象并不存在权限放大或者缩小的问题(因为赋值是拷贝),只有引用和指针才会涉及const这类问题。
在这里插入图片描述
我们发现e和d的地址并不相同,这也能验证我们之前所说的。

4.使用场景

4.1. 做参数

  1. 以后我们会学一个东西叫模板,传参是就要尽量避免传值传参,因为传值传参会进行拷贝,如果模板T是自定义类型,可能会存在深拷贝的问题,所以最好用引用传参,而引用加了const,不管参数是什么类型都可以传参,哪怕涉及类型转换。听不懂也没关系,我们后面会继续讲解模板,这里不再继续延伸了。
    在这里插入图片描述

  2. 另一个例子:引用配合上函数重载,引用参数的交换实际就是实参的交换,且配合函数重载调用起来就像在调用同一个函数。
    在这里插入图片描述

  3. 还可以做输出型参数:
    在这里插入图片描述

  4. 不带头节点的链表要改变头节点时不用传二级指针,用引用:
    在这里插入图片描述

4.2.做返回值

我们先复习一个知识点:
被static修饰的变量只会初始化一次,生命周期变为全局的,不改变其作用域。
在这里插入图片描述

4.2.1.传值返回

ok,接下来我们来讲一下返回值的具体过程。传值返回的时候会产生一个临时变量,它跟传参一样,如果小临时变量会用寄存器替代,如果大它就会在main函数的栈帧中提前开好空间。实际上返回值的类型其实就是临时变量的类型。把n拷贝给临时变量,临时变量再拷贝给ret。设计这个临时变量是不是因为没有被static修饰的局部变量出了函数作用域会被销毁呀!
在这里插入图片描述

怎么证明有没有产生临时变量呢?临时变量具有常性。
在这里插入图片描述

4.2.2.引用作为返回值

引用作为返回值,说明tmp是n的别名,tmp是int &型,没有多开辟空间且没有权限改变问题,tmp再赋值给ret。实际上就是直接把n赋值给了ret
在这里插入图片描述
如何证明上述过程?看地址:
在这里插入图片描述
但是这个代码存在问题,因为n出了函数作用域会销毁,ret也没有了意义,无法正常使用。(引用间接搞出来的野指针问题)。
第一次调用函数栈帧没有被覆盖,所以n还是1,第二次调用函数栈帧被覆盖了,所以n是随机值。其实有些编译器第一次就会清理函数栈帧,所以第一次是随机值也不奇怪。
在这里插入图片描述
注意:如果函数返回时,出了函数作用域,如果返回对象还未还给系统,则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。否则可能会出现越界问题。

4.2.3.传值返回和引用返回的区别

传值返回:会有一个拷贝。
引用返回:没有拷贝,直接返回变量的别名。

5.效率比较

5.1.传值、传引用效率比较

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。
在这里插入图片描述

5.2.值和引用的作为返回值类型的性能比较

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

6.引用和指针的区别

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

int main()
{
	int a = 10;
	int& ra = a;
	cout<<"&a = "<<&a<<endl;
	cout<<"&ra = "<<&ra<<endl;
	return 0;
}

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

int main()
{
	int a = 10;
	int& ra = a
	ra = 20;
	int* pa = &
	*pa = 20;
	return 0;
}

我们来看下引用和指针的汇编代码对比:
在这里插入图片描述
引用和指针的不同点:

  1. 引用在定义时必须初始化,指针没有要求
  2. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  3. 没有NULL引用,但有NULL指针
  4. 在sizeof中含义不同引用结果为引用类型的大小,但指针始终地址空间所占字节个数(32位平台下占4个字节)
  5. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  6. 有多级指针,但是没有多级引用
  7. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  8. 引用比指针使用起来相对更安全
  • 42
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 30
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 30
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yuucho

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值