Bootstrap

C++入门——引用(重点!!!)

引用概念

引用不是新定义一个变量,而是给已存在的变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它的引用变量共用一块内存空间。
在这里插入图片描述
类型& 引用变量名=引用实体;
(引用类型必须和引用实体是同样类型的)

引用特性

1.引用在定义时必须要初始化(必须说明是哪个变量的引用)
2.一个变量可以有多个引用
3.引用一旦引用一个实体,就不能引用其他实体

在这里插入图片描述
在main函数里面,调用swap 函数,swap的两个参数是引用类型,即cc的引用为m,dd的引用为n。当n和m,改变的时候,cc,dd也会改变。

在数据结构单链表里面,我们通常用二级指针来改变头指针。
但是如果运用了引用,把原本的struct ListNode**pphead ,变为
struct ListNode*&phead 。当phead改变的时候,对应的struct ListNode*头指针变量也会改变。
当然,在有的教材当中,会在定义结构体的时候,就会定义结构体指针变量,如图上的PTNode,所以就会定义引用PTNode&phead;会稍微难理解点。

C++,引用不能完全替代指针,C++要兼容C,所以不能把指针取代了。

常引用(const)

在这里插入图片描述

可以对常量引用

const int& cc = 10;

引用权限只能平移或者缩小,不能放大。

const int a = 10;
	int& rra = a;

const 修饰的常变量,只可读。
但是用引用rra,是int 型,(可读可写),权限放大了,就不可以了。
权限平移:

	const int a = 10;
	const int& rra = a;

	int b = 20;
	int& rrb = b;

权限缩小:

	int b = 20;
	const int& rrb = b;


void fun(int x)
{}

int main()
{
	int a = 10;
	const int b = 20;
	const int& rra = a;//rra是a的引用,rra权限缩小

	fun(a);
	fun(b);
	fun(rra);
}

这里传给x 只是拷贝,对于x的改变,不会影响a,b,rra;

void fun(int& x)
{}

int main()
{
	int a = 10;
	const int b = 20;
	const int& rra = a;//rra是a的引用,rra权限缩小

	fun(a);
	fun(b);
	fun(rra);
}

这里是别名,x的改变就会导致传过来的数改变的,但是由于b,rra 权限受限,他们只可读不可写,所以不可以。
所以一般引用做参数的时候,都是用const引用(非常有意义)

void fun(const int &x)

这样a,b,rra就都可以了。
但是如果要去修改就不能用这样了,只能保证传过来,为了去减少拷贝,但是不可以去修改值,因为有const 修饰



double d=12.34;
int sd=d;
int &rd=d;
const int&rrd=d;

不管是隐式转换还是强制转换,都会有一个临时变量
比如上面的sd,把d的值给sd,隐式转换为int,是有一个临时变量是int型的,把转换好的给临时变量,再去给sd,d本身没有变。
在这里,rd引用的不是d,而是那个临时变量(具有常性,也就是只可读),现在要i引用为int,那就是可读可写,是不可以的。
但是 变成前面有const修饰的,rrd就可以,此时的rrd就是那个临时变量的值,就是12。



关于const缺省参数:

void fun(int&a=10)//这个不可以,前后不一致
void fun(const int &a=10)//这个可以

使用场景

1.做参数(输出型参数)

在这里插入图片描述
Swap1的left和right 没有空间。
Swap2的left和right 要开空间的,是实参的临时拷贝。
引用做参数的好处:
1.减少拷贝,提高效率
2.输出型参数,函数中修改形参,实参也修改了

2.做返回值(引用返回)

传值返回

在这里插入图片描述
传值返回,都会生成一个临时变量,小的话就在寄存器,大的话就在上一层栈帧里。
该临时变量根据返回值给,比如此时返回值是int,那么这个临时变量就是int类型。
首先这个变量n是在静态区里面的,当Count函数return n的时候,把n的值放在临时变量里面,然后Count函数栈帧销毁。但此时n在静态区不影响临时变量。
在这里插入图片描述
这里的n没有static修饰,就说明此时n就在Count的函数栈帧上开辟的空间。
生成的临时变量里面存放着n的值,当Count 栈帧销毁的时候,n也会销毁,但是此时已经把值给了临时变量了,所以不影响。

引用返回

在这里插入图片描述
这里的n有static 修饰,但又看到返回值是int &,是引用类型,代表着临时变量也是一个引用类型,是n的别名。
当Count函数栈帧销毁,但是n却在静态区不受影响,所以n的别名不受影响。
在这里插入图片描述
在这里,不一样的是没有了static的修饰,那么当Count函数栈帧销毁的时候,n也会销毁,此时n的别名,这块地址不受保护
就会出现下面几个情况:
1.这块空间没有分配给别人,并且原来n这块地方也没有清除,那么可以正常访问,没有影响。
2.这块空间没有分配给别人,但是数据被清除了。
3.这块空间已经分配给了别人,别人也已经重现写了新的数据,原来的值被覆盖了,所以返回的ret是不确定的!
在这里插入图片描述
**结论:**出了函数作用域,返回变量不存在了,不能用引用返回,因为引用返回的结果是未定义的。
出了函数作用域,返回变量存在,才能用引用返回。
返回变量存在无非就是在堆上,在静态区,就是不能在栈上,不然会出问题。

引用和指针的区别

在语法上,引用就是一个别名,没有独立的空间,和其引用的实体共用一块空间。
但是在底层上,引用实际是有空间的,因为引用是按照指针的方式来实现的。
引用和指针的不同点:
1.引用概念上定义一个变量的别名,指针存储一个变量的地址。
2.引用在定义时必须初始化,指针没有要求。
3.引用在初始化时引用一个实体之后,就不能再引用其他实体了,而指针可以再任何时候指向任何一个同类型的实体。
4.没有NULL引用,但有NULL指针。
5.在sizeof中的含义不同:引用的结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下,占4个字节)
6.引用自加即引用的实体自加,指针自加即指针向后偏移一个类型的大小。
7.有多级指针,但是没有多级引用。
8.访问实体的方式不同,指针需要显式解引用,引用时编译器自己处理的。
9.引用比指针用起来更安全。

;