一、引用是什么?
引用不是新定义一个变量 ,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。
简而言之呢,引用就是 取别名,给你取一个绰号!
邓紫棋 ->解解 ->金鱼嘴
这就是引用的意思,不会再创造另外的你,还是你自己本身!!不开辟额外的内存空间。
二、初识引用
1.初始引用
//定义引用类型 int&
类型& 引用变量名(对象名) = 引用实体;
int a = 0;
int& ra = a; //帮a取别名 ra
int& rra = a; //帮a取别名rra
一个变量可以取多个别名
int a = 10;
int& ra = a; // 引用
int& x = a;
int& y = x;
x++;
y++;
a++;
a最后的结果是 13.
比如在家你妈叫你大宝,在外面别人叫你头铁,在社会别人叫你 牛哥,这是一个意思,你还是你,只不过别名多了。
但要注意的是!!
引用类型必须和引用实体是同种类型的!
double a=8.88;
int& ra=a;
这就是错误的!!
那先问一下,引用出现的好处和优点是什么呢?
为什么要有引用????
引用做参数好处:
以前我们交换两个数是这样交换的
void Swap(int *m,int*n) { int temp=*m; *m=*n; *n=temp; } int main() { int a=1; int b=2; Swap(&a,&b); return 0; }
1. 之前交换两个数,需要通过指针,即访问变量的地址,指针变量解引用来交换a,b的内容。
但是有了引用的话,只需这样即可
函数的形参类型为引用类型,m和n就是a和b的别名,相当于传过去的就是a和b,直接改变的就是a和b的值。
2. 在学数据结构的时候,我们经常会改变指针变量,涉及到二级指针和一级指针,会有些许的困难,但如果使用引用的话,就会方便很多,容易理解!
typedef struct ListNode { struct ListNode* next; int val; }LTNode, *PLTNode; //改变指针变量,需要通过传它的地址,间接通过 解引用 来改变其内容,这就会涉及到二级指针 void SlistPushBack(struct ListNode** pphead, int x) //但是如果使用引用,我们就可以直接取别名struct ListNode*& phead,别名phead,来直接改变它 void SlistPushBack(struct ListNode*& phead, int x) //PLTNode为结构体指针,所以引用可以直接PLTNode& phead,这就是一些书上为了方便理解而这样写 void SlistPushBack(PLTNode& phead, int x) {} int main() { struct ListNode* plist = NULL; SlistPushBack(&plist,1); return 0; }
所以,我们可以体会到,引用出现的一个原因就是为了淡化指针的使用,便于理解和使用,当然引用对C++的学习是非常的重要,不仅于此!!
2.引用特性
1.引用在使用定义前必须初始化。
2.一个变量可以有多个引用。
3.引用一旦引用一个实体,再不能引用其他实体。 就是说,你在家里叫宝宝,你妈妈不可能给你弟弟也叫宝宝吧,那样的话,就分不清你两谁是谁了,所以引用只能引用一个实体!
int a=10;
int&ra=a;
int b=20;
ra=b;// 这一步ra是b的引用吗?
当然不是,引用只能引用一个实体,所以这里仅是简单的赋值。
a=20这就是结果了!
3.引用的使用场景和const修饰的引用
1.做返回值
1.传值返回:
int Count() { int n = 0; n++; // ... return n; } int main() { int ret = Count(); cout << ret << endl; return 0; }
在调用函数的时候,会开辟函数栈帧,栈帧是向下生长的,如图所示:
我们观察发现,在将n返回时,会创建一个临时变量来将n的值拷贝到临时变量中,等到Count函数栈帧销毁时,变量n自然会销毁,导致无法返回,造成越界访问。所以需要一个临时变量提前在调用Count函数的main函数栈帧中,创建一个临时变量。
当然:临时变量的创建与否,需要看除了函数作用域以后,返回变量是否存在
若将返回变量用static修饰,则变量在静态区,不会随着栈帧销毁而销毁,则无需创建临时变量。
2.引用返回
返回值为引用类型:
int& Count() { int n = 0; n++; // ... return n; } int main() { int ret = Count(); cout << ret << endl; return 0; }
此时,临时变量所存的是n的别名,因为是引用返回。在n销毁后,相当于通过别名直接访问n,但n已经销毁,这就会造成非法访问。
销毁后,n的内存空间也销毁了吗?
还可以继续访问吗?
答案是ok的:n的内存空间没有销毁,仍然可以访问!!
举个例子,比如你住酒店,你退房了,不代表那个房间就炸了,而是你对它没有了访问权限,但是你仍然可以通过别的刑的方式去访问它。
那为什么第一次打印,是1,第二次是随机值,第三次是100呢?
因为n的内存空间里的内容可能没有来得及改变,没有人去使用它,所以你非法访问时,内容可能不变,仍是你之前留下的值。
第二次为什么是随机值呢?
因为cout也是函数的调用,Count栈帧销毁,但调用cout建立栈帧,之前的空间就可能会被重复利用,就会产生随机值。
第三次为什么是100呢?那可能func函数栈帧大小刚好与count一样,在重复利用时,直接覆盖,那么n的内存空间可能就是x的内存空间了,只不过更换了值。
当然一切皆有可能,这些都可能时随机值也不为过!