【C++】引用与指针
创始人
2024-05-23 11:07:49
0

专栏放在【C++知识总结】,会持续更新,期待支持🌹


引用

引用的概念

在C++中,引用的本质其实就是给一个已经存在的变量”起别名“。也就是说,引用与它所引用的对象共用一块空间。(同一块空间的多个名字)

就比如说,李逵又叫黑旋风,而黑旋风就是指李逵本人,只是名字换了而已。

引用的特性

1. 引用在定义时必须初始化

2. 一个变量可以有多个引用,但一个引用只能有一个实体对象

#include
using namespace std;
int main()
{int a = 0;//一个变量可以有多个引用,但一个引用只能有一个实体int& b = a;//不可以写成int& b;  引用必须在定义时初始化int& c = b;int& d = c;cout << &a << endl;//012FFBD0cout << &b << endl;//012FFBD0cout << &c << endl;//012FFBD0cout << &d << endl;//012FFBD0//地址相同,abcd共用同一块空间// 另外,引用类型与引用实体的类型必须一致,这里不能写为char & d = a(error)return 0;
}

扩展(函数栈帧的创建与销毁)

这里我们进行复习一下关于函数栈帧的一些知识。我们知道,在调用一个函数时,首先会在内存占用一块空间,用来创建该函数的函数栈帧,当调用结束后,该函数栈帧会被销毁,这里需要注意的是,当栈帧被销毁后,这里的空间实际上在内存中还是存在的,只不过空间的使用权不再归我们使用。并且函数栈帧的销毁,可能会对原有空间进行清理。
这里可以举个例子来理解一下,就好比说我们在酒店开了一个房间,并且在退房时把我们的电脑放在了房间里,在这里,酒店就相当于内存的存在,而我们退房的那一刻,就好比函数栈帧销毁的那一刻,但是虽然我们退房了,该房间还是实际存在的,并没有说随着我们的退房而消失,只不过不再归我们使用。并且房间里的东西也可能会被清理(也可能依然还在),加入此时我们再进行使用该房间,用是可以用,只不过肯定是不合法的,这种行为就好比空间的非法访问。

引用的使用场景

做参数进行引用(输出型参数)

所谓输出型参数,实际上就是可以影响实参的参数,就比如我们经常写的交换两个变量的值,在以前我们会使用指针来完成传址调用,从而实现形参的改变影响实参,但现在我们可以用引用来实现,如下:
//做参数来使用,由于共用同一块空间,所以这里的c实际上就是a,d实际就是b
void Swap(int& c, int& d)
{int tmp = c;c = d;d = tmp;
}
int main()
{int a = 1, b = 2;Swap(a, b);cout << a << " " << b << endl;//2 1return 0;
}

可以做返回值使用

我们先来看这样一段代码:

这里注意的是:这里的a是局部变量,生命周期会随着栈区的销毁而结束,所以这里返回的实际上并不是a,我们通过查看反汇编发现实际上是借助了一个临时变量来实现的。

那么不禁会有个疑问,假如这里的a不随着栈帧的结束而销毁,那么会不会直接返回a呢?可以试验一下:

对于这种现象,我们可以把引用作为返回值来使用,从而实现优化,写成如下格式:

//返回值
int& Test()
{static int a = 10;a++;return a;//也会产生临时变量,但是临时变量的类型是int& 也就是a的别名,即临时变量就是返回的a,减少了拷贝操作
}
int main()
{int ret = Test();return 0;
}

这就是引用返回,即在返回类型前面加上&,虽然也需要借助临时变量的存在,但是由于临时变量的类型为int& ,即临时变量就是a,所以就减少了临时变量的拷贝工作,会使效率得到提升。我们可以来验证一下:

传值返回 vs 传引用返回效率对比

#include 
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 1000000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 1000000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}int main()
{TestReturnByRefOrValue();
}

运行结果如下:

我们发现传引用返回对比传值返回,效率会有显著提高(作为参数使用时,传引用参数的效率也会高于传值作为参数的效率)

当然,传引用作为返回值的使用是有一定的限制的,我们发现上面的代码能使用传引用返回的原因在于,返回的变量不会随着作用域的销毁而销毁。假如说返回的对象出了作用域后已经销毁,则必须使用传值返回,否则返回的结果是不确定的!

并且引用用作返回时,还可以修改返回对象(后面的学习会用到很多,这里简单介绍)

如下:

#define N 10
typedef struct ARR
{int arr[N];
}ARR;int& PosARR(ARR& arr,int i)
{return arr.arr[i];//这里的arr就是main函数里的arr,不会随着PosARR函数的结束而销毁,所以可以用引用返回
}
int main()
{ARR arr;for (int i = 0; i < N; i++){PosARR(arr, i) = i * 10;//引用返回可以修改返回对象,这里的返回对象为arr.arr[i],对此进行修改}for (int i = 0; i < N; i++){cout << arr.arr[i] << " ";//0 10 20 30 40 50 60 70 80 90}return 0;
}

总结

引用可以用作参数来使用(输出型参数),也可以用作返回使用,用作返回使用时返回的对象必须是出了所在函数作用域后不会销毁的(比如static修饰的变量,全局变量,malloc......),并且引用返回时,返回的对象可以被修改。同时还可以减少拷贝提高效率。

常引用

我们要记住这样一句话:指针和引用在赋值或者初始化时,权限可以被缩小或者保持,但不可进行修改。

这是什么意思呢?通过以下代码进行了解:

    // 权限放大(error)//const int c = 2;//const 修饰的常量不可以进行修改,可以理解只具有读的属性,不具有写的属性,而d可以修改,所以权限被放大//int& d = c;//这里正确写法应为const int& d=c;//const int* p1 = NULL;//int* p2 = p1;//同上,前面加个const即可,const int* p2=p1; (√)// 权限保持const int c = 2;const int& d = c;const int* p1 =NULL;const int* p2 = p1;// 权限缩小int x = 1;//x可以进行修改,可以理解为具有读和写的属性,而x是const修饰的,只具有读的属性,权限缩小了const int& y = x;int* p3 = NULL;const int* p4 = p3;//同上

不仅如此,由于所谓临时变量具有常性(即不可被修改)的原因,也会出现以下的情况:

    int i = 0;//double& p = i;//error//由于int到double类型发生类型转换,而类型转换会产生临时变量,临时变量又具有常性(只可读)//因此在前面加上const即可const double& p=i;//(√)

这也就解释了上文说到的引用类型与引用实体的类型必须一致,同样,在函数中也一样适用:

int add(int x, int y)
{int c = x + y;return c;//实际上是借助临时变量,将c拷贝给临时变量,再将临时变量拷贝给p
}
int main()
{int a = 1, b = 2;//int& p = add(a, b);//由于临时变量具有常性的特点,所以不可以这样写//前面加上const 即可const int& p = add(a, b);return 0;
}

引用与指针

&是一个很熟悉的符号,与指针有关,用在变量前面就是取地址符号,用在类型后面则为引用符号,那么指针与引用之间是否有着什么关系呢?

int a=0;
int* p=&a;//&:取地址符
int& b=a;//&:引用

指针与引用的相同点

实际上,引用与指针,两者之间在底层实现上其实是一样的,我们可以来进行验证

当然,两者之间也存在着很大的区别。

指针与引用的不同点

首先就是在语法概念上的区别,引用只是同一个实体的不同名称,不会单独开辟空间,但是指针会在内存开辟一块4/8byte大小的空间。
引用在定义时必须初始化,指针没有要求
引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体(这一点也就意味着引用并不能实现完全替代指针,就比如在链表这里,用来指向下一个节点的变量类型,只能是指针)
有多级指针,但是没有多级引用
引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
引用比指针使用起来相对更安全(野指针)

...

看法:

因此对于指针与引用,我们只能是说引用相较于指针来说,更加容易理解使用,并且也不会存在空引用的问题,但是在一些场景下,引用自身的特点(不能改变指向)也存在着使用限制,此时就得用指针来实现

end

生活原本沉闷,但跑起来就会有风!🌹

相关内容

热门资讯

我心中的桃花源作文600字 我心中的桃花源作文600字(通用23篇)  在日复一日的学习、工作或生活中,大家都有写作文的经历,对...
为自己竖起大拇指优秀作文70... 为自己竖起大拇指优秀作文700字(通用22篇)  在日常学习、工作抑或是生活中,大家都写过作文,肯定...
作文 瓢虫 作文 瓢虫  在学校围墙外的拐角处,有一排杂树,在春光的沐浴下,变绿了。小瓢虫经不住诱惑,在这里安了...
上学路上小学三年级作文 上学路上小学三年级作文300字(精选46篇)  在日常学习、工作和生活中,大家对作文都再熟悉不过了吧...
丛林求生作文 丛林求生作文5篇  篇一:森林求生  终于,我还是迷路了。我就知道,总有一天,我会迷失在森林中的。我...
那一次我真感动作文 那一次我真感动作文(精选48篇)  在日常学习、工作和生活中,大家一定都接触过作文吧,作文根据写作时...
海底漫游记作文 海底漫游记作文(精选43篇)  无论是在学校还是在社会中,大家总免不了要接触或使用作文吧,借助作文可...
五一游记作文 五一游记作文(精选49篇)  在生活、工作和学习中,大家总免不了要接触或使用作文吧,作文根据体裁的不...
我爱长沙作文 我爱长沙作文我生在长沙,长在长沙。听大人们说原来的长沙,听别人说现在的长沙,现在长沙变化非常大,我也...
新年的作文 关于新年的作文(15篇)  在日常的学习、工作、生活中,大家或多或少都会接触过作文吧,作文是由文字组...
美丽的秋天作文 最新美丽的秋天作文范文(精选18篇)  在平凡的学习、工作、生活中,大家总少不了接触作文吧,作文是一...
给吴老师的一封信 给吴老师的一封信  老师是您辛勤教导了我们,给予了我们无穷无尽的知识,下面是小编为大家搜集整理的写给...
最美的瞬间作文600字 最美的瞬间作文600字  在日常学习、工作抑或是生活中,大家或多或少都会接触过作文吧,作文是从内部言...
厨房变形记作文 厨房变形记作文········ 厨房变形记····这个纪录片主要讲的是一个城市公子哥易虎臣和一个在贫...
下象棋 下象棋下象棋1我的课余爱好有很多,有玩电脑、看课外书、跑步……其中最喜欢的是下中国象棋。     现...
我学会了炒菜作文400字 我学会了炒菜作文400字(精选70篇)  在平平淡淡的日常中,大家对作文都不陌生吧,作文是由文字组成...
炎热的夏天作文 炎热的夏天作文(精选40篇)  在平平淡淡的学习、工作、生活中,大家都经常看到作文的身影吧,根据写作...
画笔 画笔画笔1  在我的童年中,有许许多多的童年趣事,它们就像一支支五彩缤纷的画笔,在人生的图画上添上了...
关于信守承诺的作文 关于信守承诺的作文800字(精选15篇)  在日常学习、工作抑或是生活中,许多人都有过写作文的经历,...
我熟悉的人作文   我熟悉的人作文(一)  我现在是一名四年级的小学生,说到熟悉的人,我的脑海里自然浮现了妈妈的身影...