【引用】引用的概念与基本使用原则

一、引用的基本介绍

 什么是引用?通俗的讲,引用就是"取别名",我们看下面的例子:
在这里插入图片描述
 可以看到,引用并不会开辟新的空间。


二、引用的基本特性

特性①:必须有初始化
在这里插入图片描述
特性②:一个变量可以有多个引用
在这里插入图片描述
可以给别名取别名,但本质上都是对a的引用
特性③:一旦引用了一个实体,就不能引用其他实体
在这里插入图片描述
可以看到,之后b不会再引用实体c,只能对b做赋值修改


三、引用的基本原则

①引用类型和引用实体类型相同

 引用的一个极其重要的基本原则是:对变量实体的读写权限只能缩小不能放大。我们在接下来的例子中来说明这个原则。
 引用可以引用常量吗?答案是可以的。但是我们需要加上const来保证读写权限没有被放大:
在这里插入图片描述

✪代码分析
 我们来分析这段代码我们需要注意到,引用就是给实体取的别名,二者在地址上是一样的,对别名的修改也会改变实体的值,所以我们在取别名的时候就要保证对引用的读写权限没有扩大。就像上面的例子,10是一个只读变量,如果不加上const,那么就意味着可以通过a来修改常变量10,这显示是不行的。

②引用类型和引用实体类型不同

 引用类型必须和引用实体是同种类型吗?不一定!怎么理解这个问题呢?我们可以类比我们在C语言中学到的隐式类型转化,我们来分析中间的过程:

int main()
{
	double d = 6.66;
	int a = d;
}

 为什么可以将类型为double的变量赋值给类型为int的变量呢?实际上这个赋值过程不是一步到位的,而是存在一个 “中间变量”。这个中间变量接收d的整数部分,最后再把这个中间变量拷贝给a。需要注意的是,这个中间变量具有常属性。引用也是同样的道理:
在这里插入图片描述

✪代码分析
 a并不是直接引用d,而是引用这个中间变量。由于中间变量具有常属性,所以前面必须加上const修饰避免扩大读写权限。
在这里插入图片描述
 通过取地址我们可以确认a没有引用d,从而也间接说明了中间临时变量的存在。


四、引用的基础应用

①对标传址操作
void swap(int& a, int& b)
{
	int tmp = a;
	a = b;
	b = tmp;
}

int main()
{
	int a = 10;
	int b = 20;
	swap(a, b);
	return 0;
}

 正是由于引用和引用实体存在互相改变的关系,所以我们可以用引用来替代传值操作。拿我们C语言中常写的swap函数举例,这样写起来是不是更爽了呢?
 但如果设计的函数中需要传入常数,那么就必须在参数前面加上const,否则存在扩大实体读写权限的风险。经过实际测试,引用的效率和传址的效率是差不多的。
 相应的,一些输出型参数也可以用引用来实现,例如:

int* sortArray(int* nums, int numsSize, int* returnSize){

}

 returnSize是我们的输出型参数,在C语言中我们只能通过传址的方式实现,现在用引用也可以实现这样的效果。

②作为返回值
int& count()
{
	static int n = 0;
	n++;
	return n;
}

 用引用作为返回值和传统的方式相比有什么好处呢?我们先来谈谈传统的方式是如何传递返回值的:

  • 首先将预备返回的值存储到临时变量,再将临时变量拷贝给用于接收函数返回值的变量
  • 预备返回的变量小则用寄存器存储,变量大则则创建函数栈帧的时候预先为返回值创建存储空间

 如果我们使用引用,那我们根本就不需要额外的临时空间和额外的拷贝,因为我们是通过引用直接对实体进行操作的,这可以提供程序的效率。
在这里插入图片描述

 但是引用作为返回值只适合返回值出作用域不销毁的情况,如上面static修饰的n变量,如果我们我们引用的变量出函数作用域销毁,那我们实际上通过引用创建了间接的“野指针”

int& Add(int a, int b)
{
	int c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1, 2): " << ret << endl;
	cout << "Add(1, 2): " << ret << endl;
	return 0;
}

在这里插入图片描述

 ret的值可能是随机值也可能不是,取决于这块空间有没有被重新覆盖和使用。cout函数栈帧的创建覆盖了Add函数的函数栈帧,所以ret的值变成随机值了

再来看几个变式:
int& Add(int a, int b)
{
	static int c;
	c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1, 2): " << ret << endl;
	cout << "Add(1, 2): " << ret << endl;
	return 0;
}

在这里插入图片描述

 变量c用static修饰后,这块空间出函数作用域后不会被销毁,不用担心被其他函数栈帧所覆盖

int& Add(int a, int b)
{
	static int c = a + b;
	return c;
}

int main()
{
	int& ret = Add(1, 2);
	Add(3, 4);
	cout << "Add(1, 2): " << ret << endl;
	cout << "Add(1, 2): " << ret << endl;
	return 0;
}

在这里插入图片描述

注意static语句只在初始化时执行一次

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

罅隙`

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

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

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

打赏作者

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

抵扣说明:

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

余额充值