C++关于引用的分析

目录

1. 引用的概念

2. 引用的特点

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

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

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

3.引用的应用

3.1 做参数

3.2做返回值

3.2.1 函数栈帧及临时变量

3.2.2 引用 的越界访问

3.2.3 引用做返回值适合的场景

3.2.4 引用返回的优点:

4.常引用

常引用的特殊之处

5. 引用和指针的区别


1. 引用的概念

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

简单理解为,给一个人起了一个小名 ,你叫他大名还是小名,本质上都是叫他。

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


int main()
{
	int a = 10;
	int& b = a;  // 引用,给 a 起了一个别名 , b的实质还是  a .

	return 0;
}

我们需要注意的是,引用类型和引用实体必须是同一类型。引用只是起了一个别名,本质还是它,类型肯定保持相同。

2. 引用的特点

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

引用只是给变量起个别名 ,如果变量都不存在,别名也没有意义,引用时,必须初始化

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

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

我们定义变量b引用a 后,再对c引用,程序就会报错。

3.引用的应用

3.1 做参数

在此之前,我们了解到的传参方式有两种,传址,传址。接下来,我们会了解到第三种,传引用。

int Swap(int x, int y)    //  传值 调用  没有实现交换功能
{
	int tmp = x;
	x = y;
	y = tmp;
}

int Swap(int* x, int* y)  // 传址 调用 ,实现交换功能
{
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

int Swap(int& x, int& y)   // 传引用 ,实现交换功能
{
	int tmp = x;
	x = y;
	y = tmp;
}

int main()
{
	return 0;
}

我们可以看到上述的三个Swap函数,分别使用了,传值,传址,传引用。

传值调用,形参是实参的一份临时拷贝,对于形参的改变不会影响实参,没有实现交换功能。

传址,传引用 ,一个是通过地址拿到了它 ,一个直接是它本身(不过是用别名而已),都可以直接改变它,实现交换功能。

调用函数,需要改变实参时,我们推荐使用传址,或者传引用。

我们可以判断一下,上述的三个函数有没有构成函数重载

答案是,肯定的。但是没完 ,

 虽然它们的参数不同,构成函数重载,但是 我们在调用函数时,发现,系统报错,调用时存在歧义,系统无法判断,我们使用的是传值,还是传引用。这是我们需要注意的知识点。

3.2做返回值

我们以前写的函数,返回值通常只有一个返回值的类型,如果加上& ,即引用作返回值。

int Add(int x, int y)
{
	int c = x + y;
	return c;
}


int& Add1(int x,int y)
{
	int c = x + y;
	return c;
}


int main()
{
	int a = 1;
	int b = 2;
	return 0;
}

以上面=的代码为例,

实现正常的增加函数,直接返回一个int 类型的数据。后面会结束做返回值的优点。

3.2.1 函数栈帧及临时变量

我们需要了解函数栈帧相关的知识。

简单的说,函数只有在调用它的时候才会在栈上开辟空间,来实现功能,在函数运行结束时,销毁。以Add为例,调用时,在栈上开辟空间,运行,得出c  = 3,函数运行结束,在栈的空间被销毁。

那么大家可能有疑惑,函数栈帧被销毁了,那么返回的是什么呢。

对于有返回值的函数,通常都会有临时变量,用来存放需要返回的数据,不至于在函数栈帧销毁时丢失数据。

较小的返回值,其临时变量通常存在寄存器中。(例如 int ,double 这种类型的返回值较小,直接存放在寄存器中)

较大的返回值 ,其临时变量通常存在于,调用函数的栈帧上。(例如 返回值类型为较大的结构体)

Add函数的返回值 ,c 它其实是拷贝了一份临时变量在寄存器中,虽然Add函数栈帧在函数运行结束时被销毁,但是其临时变量存于寄存器。

3.2.2 引用 的越界访问

Add1 函数的栈帧创建和销毁 和 Add 函数一样。那么我们运行一下

int main()
{
	int a = 1;
	int b = 2;
	int &c = Add1(a, b);
	cout << c << endl;
	
	return 0;
}

 好像没有问题。但是我们仔细想一下,传引用,此时返回的本质上就是c ,不是它的临时变量,函数栈帧被销毁时,此时 c 已经被销毁,我们任然访问c ,这属于越界访问,编译器对于越界访问不一定可以检测到,

编译器往往可以检测出越界写,不一定可以检测出越界读。

此时访问已经属于越界访问,但是我们为什么得到了正确返回值。

函数栈帧在销毁时,不一定将内容都重置,重置与否,取决于编译器,我们可以看到,VS2019

是不会重置的,


int main()
{
	int a = 1;
	int b = 2;
	int &c = Add1(a, b);
	cout << c << endl;
	printf("111111111111111111111111111111111111111111111111111111111111111111111111111111\n");
	cout << c << endl;
	return 0;
}

此时我们再运行

 此时,我们可以看到 c 的值 是随机值,应该是因为我们中间的打印操作,造成了 c的内存空间的重置。

3.2.3 引用做返回值适合的场景

首先我们需要保证,不会造成越界访问。

这就需要我们的返回值是个全局变量,或者,它的作用域不会随着函数栈帧的销毁而销毁。

函数返回时,出了函数作用域,返回对象还在(即没有返回给系统),我们可以使用引用返回,

如果返回的对象,已经返回给系统,只能使用传值返回。

3.2.4 引用返回的优点:

1.提高效率

传值返回利用的是 拷贝的临时变量,如果这个数据特别庞大,再多拷贝一份,效率低下 ,会引起不必要的浪费,在后续我们接触到一些特别大的需要处理的数据,如果可以使用引用返回,推荐使用。

2.可以修改返回变量 (做到可读可写)

举个例子



int& test(int i)
{
	static int a[100];
	return a[i];
}

int main()
{
	for (size_t i = 0; i < 10; i++)
	{
		test(i) = 10 + i;
	}
	for (size_t i = 0; i < 10; i++)
	{
		cout << test(i) << endl;
	}
}

a数组是全局变量,函数运行结束后不会销毁,返回的是引用,在拿到返回的引用后,再赋值,做到了 可读可写

 

4.常引用

我们需要考虑权限问题


int main()
{
	const int a = 1;
	int& b = a;  //   a ,不可修改 ,权限放大,报错

    int c = 1;

	const int & d = c;   // c 本身可修改, d 不可修改,属于权限的缩小,可以运行

	

	return 0;
}

使用引用时,需注意权限的变化。

其次,传引用作为参数时,如果不需要改动,我们可以加上 const 来修饰,避免因为错误改动,提升代码的健壮性。

常引用的特殊之处


int main()
{
	double a = 1.1;
	const int& b = a;
	return 0;
}

我们尝试运行这样一段代码,但是我们先判断一下,引用的要求是,引用的类型和引用的实体必须是同一类型,但是我们运行程序后发现并没有报错,这是为什么?它们明明是不同的类型。

在C语言中,将一个double类型的数据的值赋给一个 int类型的数据,会发生隐式类型转换,将其double类型中的int数据拷贝进临时变量,再进行赋值。完成隐式类型转换。

临时变量有常属性,不可修改。此时我们使用const来修饰,这样权限是相符合的,完成引用。

那么const修饰的引用就可以做到通吃。即使类型不同,引用的是,临时变量的别名。

即 const引用 , 可以接收各种类型的变量

5. 引用和指针的区别

引用是 给一个变量取别名 ,它仍然指向同一块内存空间。

指针是开辟一个空间,存放其他变量的地址,存放的地址。

引用在定义时,必须初始化,指针在定义时,即使不初始化,程序也不会报错

指针存在NULL,引用不存在空。

存在多级指针,不存在多级引用。

引用在引用了一个实体以后就不能在引用其他实体,指针随时随地都可以指向同一类型的实体。

由于指针特别灵活,一旦使用错误,比如越界,就会导致出现bug,引用的安全性相对较高。

谢谢观看,评论区欢迎讨论。

 

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值