关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

【C++】 夜的尽头不是 引用,是天空没有极限(上)

发布时间:2023-06-29 11:00:57

一、引用是什么?

引用不是新定义一个变量 ,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空

间,它和它引用的变量共用同一块内存空间。

简而言之呢,引用就是 取别名,给你取一个绰号!

 邓紫棋 ->解解 ->金鱼嘴

这就是引用的意思,不会再创造另外的你,还是你自己本身!!不开辟额外的内存空间。


二、初识引用

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的内存空间了,只不过更换了值。


当然一切皆有可能,这些都可能时随机值也不为过!



/template/Home/leiyu/PC/Static