c++中引用的全面理解及注意事项(清晰易懂)

一、什么是引用?引用是干什么的?

所谓引用,就是给变量起一个别名,这个别名就相当于这个变量的另一个名字,也代表这个变量。这也就是说,他们是同一个变量,也就共用同一块空间,并不会开辟一个新的空间。

二、引用 的使用

数据类型 &别名 = 原名

int a=10;
int &b=a; //代表b是a的引用.也就是b是a变量的另一个别名
cout<<"a="<<a<<endl; //结果是a=10
cout<<"b="<<b<<endl; //结果是b=10

相当于下图这种形式

b=100;
cout<<"a="<<a<<endl; //打印出来结果是100
cout<<"b="<<b<<endl; //打印出来结果也是100

当我们修改引用的赋值时,变量的值也会更改,这说明它们共用同一块空间,代表同一个变量。

三、引用的注意事项

1.引用必须初始化

2.引用一旦初始化后,不能更改 

3.一个变量可以有多个引用(相当于给一个变量起了多个别名)

int a=10;
int b=20;
//int &c; 这种写法是错误的,声明引用的时候没有初始化这个引用
int &c=a; //这种写法是正确的,它代表,c是a的引用

那为什么必须要给引用初始化呢?

我们之前说过,创建引用的时候并不会开辟一块内存空间,也就是它并不存在自己单独的实体,它依附于变量的内存空间,若我们不给它初始化,那它相当于一块虚无,并无意义。所以,c++并不会允许我们这么去写,它会提醒你需要初始设定值。

int a=10;
int b=20;
int &c=a;
c=b; //并不会把从a的引用变成b的引用,而是将b变量的值赋给a变量
cout<<"a="<<a<<endl; //a=20
cout<<"c="<<c<<endl; //c=20;
cout<<"b="<<b<<endl; //b=20;

执行结果:

如果c的值变为20,我们并不能看出c的引用是否改变,但是a的值也被改变了,说明c还是a变量的引用,如果还是觉得不清晰,我们在这个操作之后再来改变a的值,看c是否改变,就能看出c是谁的引用。

int a = 10;
int b = 20;
int& c = a;
c = b; //并不会把从a的引用变成b的引用,而是将b变量的值赋给a变量
cout << "a=" << a << endl; //a=20
cout << "c=" << c << endl; //c=20;
cout << "b=" << b << endl; //b=20;
cout << "更改a和b的值后,引用c的值" << endl;
a = 50;
b = 100;
cout << "c=" << c << endl;

我们可以看到c的值与a的值相同,而与b的值无关,这说明,引用c并没有被更改,也就是第二条,初始化后不可更改,而c=b;这种操作则会被理解为赋值操作

四、引用的本质

其实上述注意事项中,引用在初始化之后不可更改是因为引用的本质在c++内部实现是一个指针常量.

1.什么是指针常量

顾名思义,就是指针类型的常量,主语是常量,修饰词是指针类型,也就是说指针类型的这个值是不可更改的,指针类型的值就是指向的地址,也就是指针的指向不可更改,原来指向a,不能将它改为指向b。

使用

数据类型 * const 变量名

// 例如int *const test,这个就是指针常量

这也就解释了为什么引用初始化后不可更改,本质是指针常量,指向就不能改变,在c++内部,如果发现是引用,就会转换为指针常量。

2.什么是常量指针

常量指针,也就是常量类型的指针,相当于常量的指针,代表指针指向的是一个常量,这就说明,指针解引用之后的值是不可更改的,要与指针常量进行区别。

使用

const 数据类型 *变量名

const int *test;

 两个作用相加就是常量指针常量,不仅指向不能被修改,解引用之后的值也不能被修改。

使用

const int * const test;

五、引用做函数参数

1.作用:函数传参时,可以利用引用的技术让形参和实参代表的是同一块内存

2.优点:可以简化指针修改实参

//1. 值传递
void Swap01(int a, int b) {
	int temp = a;
	a = b;
	b = temp;
}

//2. 地址传递
void Swap02(int* a, int* b) {
	int temp = *a;
	*a = *b;
	*b = temp;
}

//3. 引用传递
void Swap03(int& a, int& b) {
	int temp = a;
	a = b;
	b = temp;
}

int main() {

	int a = 10;
	int b = 20;

	Swap01(a, b);
	cout << "a:" << a << " b:" << b << endl;

	Swap02(&a, &b);
	cout << "a:" << a << " b:" << b << endl;

	Swap03(a, b);
	cout << "a:" << a << " b:" << b << endl;

结果:

可以看到,地址传递将a,b的值进行了交换,此时实参a,b的值应该是a=20,b=10,然后进行引用传递之后,a=10,b=20,值又换回来了,可以说明,引用传递和地址传递一样,都会改变实参的值,而值传递则不会改变实参的值,而且引用传递不需要解引用,使用起来比较方便。

六、引用做函数返回值

使用

数据类型 & 函数名(){return 变量名 }

注意事项:

1.要在定义函数的时候,在函数名前加&

2.不能返回局部变量的引用

3.如果函数做左值,必须返回引用

对于第一点,因为返回值是引用,所以函数类型应该也要有&

对于第二点,如果返回局部变量的引用,可以看到打印出来的结果是错误的

//返回局部变量引用
int& test01() {
	int a = 10; //局部变量
	return a;
}
int main() {

	//不能返回局部变量的引用
	int& ref = test01();
	cout << "ref = " << ref << endl;
	cout << "ref = " << ref << endl;
    return 0;
}

 结果

为什么会出现这种情况呢?

 我们知道函数调用之后就会结束进程,而引用并没有实体空间,而ref变量已经被销毁了,再打印引用就会出现是随机值的情况(有的编译器第一次调用结果是对的,第二次调用是随机值)

正确使用方法(返回静态变量的引用)

//返回静态变量引用
int& test02() {
	static int a = 20;
	return a;
}
int main(){
int& ref2 = test02();
	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;
    return 0;
}

第三点,一般都是函数做右值,也就是将函数的返回值赋值给变量,那函数做左值就是函数的返回值会被赋值,普通函数是不可实现的

但是引用作为返回值,就可以这么使用

#include <iostream>
using namespace std;
int &test02() {
	static int a = 10; 
	return a;
}
int main() {

	int& ref2 = test02();
	test02() = 1000;

	cout << "ref2 = " << ref2 << endl;
	cout << "ref2 = " << ref2 << endl;


	return 0;
}

结果

七、引用和指针的区别

1.引用必须被初始化,指针可以选择不初始化

2.引用指向不能被修改,指针指向可以被修改

3.访问实体的方式不同,引用是编译器处理,指针需要自己解引用

4.引用不可以为空,指针可以为空

5.引用比指针更安全(指针野指针,空指针,悬挂指针)

6.sizeof引用得到的是所指向变量(对象)的大小,sizeof指针得到的是指针的大小

7.引用作为函数参数传递时,传递的是实参本身,指针作为函数参数传递时,传递的是指针变量的值

8.引用比指针使用起来更简洁

9.引用不能有多级,指针有多级指针

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值