您现在的位置是:首页 >其他 >【C++】引用网站首页其他

【C++】引用

蛋超饭不要加蛋 2024-06-17 10:13:42
简介【C++】引用

目录

?一.引用的概念?

?二.引用的特点 ?

?三.常引用?

?四.引用的应用?

4.1做参数

4.2做返回值

?五.引用和指针的区别?


?一.引用的概念?

        引用不是定义一个新的变量,只是给一个变量其别名,其和其引用的变量公用一块内存空间

        在我们日常生活中,经常会给身边的朋友和同学起外号,这里的起外号就相当于引用,比如鲁迅和周树人指的是同一个人

        我们定义一个变量a和它的引用b,分别输出两者的值和地址

#include<iostream>
using namespace std;
int main()
{
	int a = 10;//定义一个变量a
	int& b = a;//b引用a
	cout << a << endl;//输出a的值
	cout << b << endl;//输出b的值
	cout << &a << endl;//输出a的地址
	cout << &b << endl;//输出b的地址
	return 0;
}

输出结果

可以看到两者的值和地址都一样,因此验证了引用和引用的变量共用一块内存空间 

示意图如下

即一块空间有两个名字

?二.引用的特点 ?

        2.1引用必须初始化

        我们在定义整形、指针等变量的时候可以定义的时候不初始化,不初始化的变量则是一个随机值,而在定义引用的时候必须初始化,即必须指明引用的是哪个变量,就像起外号一样,需要知道这个外号是给谁起的一样

        以下代码编译错误

#include<iostream>
using namespace std;
int main()
{
	int a = 10;//定义一个变量a
	int& b ;//定义一个引用,但没有初识化
	return 0;
}

        

 报错信息显示必须初始化引用

2.2一个变量可以被多次引用

        类似于取外号一样,一个人可以有很多个外号,所有的外号指的都是同一个人,一个变量同样可以有很多个引用,即一块内存空间有多个名称

        我们对一个变量定义多个引用,然后输出它们的值和地址

#include<iostream>
using namespace std;
int main()
{
	int a = 10;//定义一个变量a
	int& b=a ;//b引用a
	int& c = a;//c引用a
	int& d = a;//d引用a
	cout << a << endl;//输出它们的值
	cout << b << endl;
	cout << c << endl;
	cout << a << endl;
	cout << &a << endl;//输出它们的地址
	cout << &b << endl;
	cout << &c << endl;
	cout << &d << endl;
	return 0;
}

运行结果

可以看到,变量a和它的3个引用的值和地址全都是一样,验证了一个变量可以有多个引用 

2.3引用一旦定义不可更改指向

        我们给一个人起个外号,比如叫他小胖,引用的特性就相当于小胖这个外号就和他绑定了,只能指他一个人,不能用来叫别人

        以下代码编译报错

#include<iostream>
using namespace std;
int main()
{
	int a = 10;
	int c = 20;//定义一个变量a
	int& b=a ;//b引用a
	b = c;//b引用a改为b引用c,其实就是一个赋值操作,并没有改变引用的指向
	&b = c;//编译报错
	return 0;
}

由此验证引用一旦定义则不可改变指向 

?三.常引用?

        在c语言中有常变量,常指针等概念,它们都是利用const修饰而成,在C++中,用const修饰的引用称之为常引用

        常引用即引用的变量不可以通过引用修改,即引用的变量变为只读

        以下代码编译错误

#include<iostream>
using namespace std;
int main()
{
	int a = 10;//定义一个变量a
	const int& b = a;//定义一个常引用引用变量a
	b = 20;//通过引用修改a的值
	return 0;
}

由于定义了常引用引用变量a,故此时变量a对于引用来说作为只读变量,不可通过引用修改

在此引入权限放大、平移、缩小的概念

权限放大即变量为只读,而引用却是可读可写,即引用的权限大于变量的权限,此时编译报错

以下代码编译错误

#include<iostream>
using namespace std;
int main()
{
	 const int a = 10;//定义一个常变量a
	 int& b = a;//定义一个引用引用变量a
	 return 0;
}

 由于变量a为常变量,即变量本身变量为只读,而定义的引用却是可读可写,发生了权限放大,故编译报错

        以上代码解决方法为把引用换为常引用

#include<iostream>
using namespace std;
int main()
{
	 const int a = 10;//定义一个常变量a
	 const int& b = a;//定义一个常引用引用变量a
	 return 0;
}

变量a的权限为只读,常引用的权限也为只读,编译则正常通过,该种情况成为权限平移

权限平移即变量和引用的权限相同

以下代码正常编译

#include<iostream>
using namespace std;
int main()
{
	 const int a = 10;//定义一个常变量a
	 const int& b = a;//定义一个常引用引用变量a
	 int c = 20;//定义一个可读可写的变量
	 int& d = c;//定义一个可读可写的引用
	 return 0;
}

变量a和它的引用权限相同,都是只读,变量c和它的引用权限相同,都是可读可写

权限缩小即引用的权限小于变量

以下代码编译正常

#include<iostream>
using namespace std;
int main()
{
	 int a = 10;//定义一个常变量a
	 const int& b = a;//定义一个常引用引用变量a
	 return 0;

变量a的权限为可读可写,而引用的权限为只读,引用的权限小于变量

总结:引用的权限可以为权限平移和权限缩小,不能为权限放大,即引用的权限必须小于等于变量

?四.引用的应用?

4.1做参数

        以前我们实现交换两个数的函数,由于形参的改变不会影响实参,所以我们需要将形参设置为指针类型,而C++中可以将参数设置为引用,由于形参是实参的临时拷贝,需要时间和空间的开销,而引用只是实参的别名,可以直接操作实参,大大地提高了调用函数的效率

        下面以交换函数为例

#include<iostream>
using namespace std;
void Swap(int& x, int& y)
{
	int tmp = x;
	x = y;
	y = tmp;
}
int main()
{
	 int a = 10;
	 int b = 20;
	 cout << a << " " << b << endl;//交换前
	 Swap(a, b);
	 cout << a << " " << b << endl;//交换后
	 return 0;

 可以看到我们利用引用作为函数参数,实现了两个数的交换

4.2做返回值

        我们知道函数传返回值的过程是产生一个临时变量,然后将临时变量赋值给函数调用处,这样也会产生拷贝的过程,而传引用则解决了这个问题

首先来分析一下下面的代码

#include<iostream>
using namespace std;
int& Add(int x, int y)
{
	int z = x + y;
	return z;
}
int main()
{
	 int a = 10;
	 int b = 20;
	 int& ret = Add(a, b);
	 cout << ret << endl;
	 cout << ret << endl;
	 return 0;
}

 可以看到第一次输出的结果是正确地,第二次输出的结果为一个随机值

这是为什么呢?

        由于Add函数的z变量为局部变量,调用完函数之后空间就销毁了,注意空间销毁并不真的将空间销毁了,而是空间的使用权没有了,也就是空间依然在那,但是我们不能使用那块空间了,但是此时我们返回了它的引用就相当于野指针,通过引用访问就是非法访问,而期间这块空间可能被操作系统分配给了其他的函数,因此非法访问的值就是随机的

        类似于我们订酒店一样,我们订了酒店之后就有了房间的使用权,退房时我偷偷地复制了一份钥匙,退房之后我就没有房间的使用权了, 但是我偷偷地用复制的钥匙再去开门就属于非法访问,此时我非法访问后可能这件房间在我退房之后没有人住,所以里面的样子和我退房时一样,也可能在我退房后清洁阿姨搞了卫生,也可退房之后被其他人使用了,所以非法访问的值是随机的,即结果是未定义的

       我们在上述代码将函数的返回值改为Int,其他不变

#include<iostream>
using namespace std;
int Add(int x, int y)
{
	int z = x + y;
	return z;
}
int main()
{
	 int a = 10;
	 int b = 20;
	 int& ret = Add(a, b);
	 cout << ret << endl;
	 cout << ret << endl;
	 return 0;
}

此时编译报错

 报错信息显示不是常引用的初始值必须为左值,即是可以修改的

原因分析:Add函数的z变量在函数调用完空间销毁,函数会设置一个临时变量进行拷贝然后赋值给函数调用处,而这个临时变量具有常量性,既可以视为常量,即为只读变量,而它的引用的权限是可读可写,属于权限放大,因此编译报错

总结:当函数调用后,引用的变量依然存在时,则可以返回引用,如静态变量、全局变量、其他函数栈帧的变量,其他情况只能使用传值返回

?五.引用和指针的区别?

        引用在语法概念上是起别名,与引用的变量共用一块空间,但是在具体的物理层面,引用其实是开了空间的,其底层是利用指针的方式实现的

我们分别定义一个引用和指针,然后通过调试查看它们的汇编代码

#include<iostream>
using namespace std;
int main()
{
	 int a = 10;
	 int b = 20;
	 int& c = a;
	 int* d = &b;
	 return 0;
}

汇编代码

 可以看到引用和指针的底层实现即汇编代码逻辑是一样的

引用和指针的区别

1.引用定义时必须初始化,指针定义时可以初始化也可以不初始化

2.引用在定义后不可以改变指向,而指针可以改变指向

3.没有空引用,但是有空指针

4.sizeof的作用不用,sizeof(引用)求的是引用的变量的大小,sizeof(指针)求的是指针变量的大小

5.引用++是引用的变量++,指针++则是向后偏移相应的位置

6.引用没有多级引用,指针有多级指针

7.引用比指针使用起来更安全

好啦,关于引用我们就学到这,如果对您有所帮助,欢迎一键三连,您的支持是我最大的创作动力!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。