分享

UC头条:C 中的引用&

 cnzrp 2023-05-31 发布于山西

本文将讲述,C++中的引用&

作者:迷茫的启明星

欢迎关注:点赞收藏✍留言

家人们,码字不易,你的点赞收藏关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

持续更新中~

引用&

引用的概念

引用并不是定义一个新的变量,而是给已经存在的变量取一个别名,相当于外号,编译器不会给它开辟内存空间,它和它所引用的变量共用一块空间。

比如说:宋江,江湖人称“及时雨”,本质都是宋江,不过是一个外号

引用的格式:

类型&引用变量名=引用实体;

voidtest1

{

inta=1;

int&b=a;//定义引用的类型

cout<<>

cout<<>

}

```

这里要注意的是,引用的类型必须和引用的实体是同种类型的。

引用的特性

引用在定义的时候必须初始化

也就是说它引用的是已经存在的变量(空间)

一个变量可以有多个引用

也就是说给一个实体取多个“外号”

引用一旦引用一个实体,就不能再引用其他的实体了

也就是说不能再把这个“外号”给别人了

voidtest2{inta=1;intx=2;//int&b;//这个引用没有初始化,编译会报错int&b=a;int&c=a;//多个引用(多个外号)//int&c=b//引用多个实体,不同实体有相同的外号,会报错}

常引用

intmain{//权限放大不可以constinta=10;int&b=a;//权限不变可以constinta=10;constint&b=a;//权限的缩小可以intc=10;constint&d=c;return0;}

在引用时要注意,只能把自己有的给别人,自己都没有权限怎么给呢?

引用的应用

在之前我们实现链表是使用指针来做参数的,现在我们就可以利用引用的性质来实现它。

譬如:实现尾插

之前使用指针是这样的(仅展现部分代码,理解就好)

voidSListPushBack(SLTNode**pphead,SLTDateTypex){SLTNode*newnode=BuyListNode(x);if(*pphead==NULL){*pphead=newnode;}else{//找到尾节点SLTNode*tail=*pphead;while(tail->next!=NULL){tail=tail->next;}tail->next=newnode;}}intmain{SLTNode*plist=NULL;SListPushBack(&plist,1);SListPushBack(&plist,2);SListPushBack(&plist,3);SListPushBack(&plist,4);return0;}

现在我们就可以这样写

voidSListPushBack(SLTNode*&pphead,SLTDateTypex){SLTNode*newnode=BuyListNode(x);if(pphead==NULL){pphead=newnode;}else{//找到尾节点SLTNode*tail=pphead;while(tail->next!=NULL){tail=tail->next;}tail->next=newnode;}}intmain{SLTNode*plist=NULL;SListPushBack(plist,1);SListPushBack(plist,2);SListPushBack(plist,3);SListPushBack(plist,4);return0;}

pphead是plist的别名,改变pphead就是改变plist

做参数

使用引用的好处就在于,没有指针那么复杂,还要使用二级指针,难以理解。再就是,定义函数时,要么就是传值,要么就是传址,现在就有了第三种情况传引用。

voidswap(intr1,intr2)//传值{inttmp=r1;r1=r2;r2=tmp;}

voidswap(int*p1,int*p2)//传地址

{

inttmp=*p1;

*p1=*p2;

*p2=tmp;

}

voidswap(int&r1,int&r2)//传引用

{

inttmp=r1;

r1=r2;

r2=tmp;

}

引用又有了另一个好处

它不需要开辟新的空间,是在原空间操作,这在数量级小的时候不起眼,可是在递归时就显得尤为可贵了,虽说传地址所需空间也少,但是在有时候,还是不如&方便。

#includestructA{inta[10000];};voidTestFunc1(Aa){}voidTestFunc2(A&a){}intmain{Aa;//以值作为函数参数,每次都要拷贝40000bytesize_tbegin1=clock;for(size_ti=0;i<1000000;++i)TestFunc1(a);size_tend1=clock;//以引用作为函数参数,无需拷贝size_tbegin2=clock;for(size_ti=0;i<1000000;++i)TestFunc2(a);size_tend2=clock;//分别计算两个函数运行结束后的时间cout<<'TestFunc1(A)-time:'<<><><><><>

点击加载图片

这里还要注意一个点,如果使用引用传参,函数中不改变参数的值,建议使用const&

做返回值

#includestructA{inta[10000];};Aa;//值返回--每次拷贝40000byteATestFunc1{returna;}//引用返回--没有拷贝A&TestFunc2{returna;}intmain{//以值作为函数的返回值类型size_tbegin1=clock;for(size_ti=0;i<100000;++i)TestFunc1;size_tend1=clock;//以引用作为函数的返回值类型size_tbegin2=clock;for(size_ti=0;i<100000;++i)TestFunc2;size_tend2=clock;//计算两个函数运算完成之后的时间cout<<'TestFunc1time:'<<><><><><>

点击加载图片

使用引用作返回值时,看上去是不是觉得效率提升了很多?但是这里有个大坑,虽说传值返回会生成拷贝,但是它在结束时会产生一个临时变量,临时变量再给调用的地方,至少是安全的

这里提一下临时变量存在哪里

如果比较小(4/8),一般是存在寄存器中

如果比较大,就会放在调用函数的栈帧中

接前面继续讲

引用做返回值,它引用的是什么呢?

是返回值,但是我们要知道,函数结束后,栈帧就销毁了,

它这个时候就相当于非法访问了,它访问的就可能是随机值(编译器不同而不同),

那么我们应该怎么使用它呢?

需要记住这个规则:如果函数返回时,出了函数作用域,如果返回对象还在(还没还给系统),则可以使用引用返回,如果已经还给系统了,则必须使用传值返回。

比如:

int&count

{

staticintn;//静态

n++;

retuernn;

}

它就可以使用引用作返回值

引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。

intmain{inta=10;int&ra=a;cout<<'&a='<<&a<<>

但是,在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

intmain{inta=10;int&ra=a;ra=20;int*pa=&a;*pa=20;return0;}

我们来看下引用和指针的汇编代码对比

点击加载图片

引用和指针的不同点:

引用概念上定义一个变量的别名,指针存储一个变量地址。

引用在定义时必须初始化,指针不是必须

引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体

没有NULL引用,但有NULL指针

在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)

引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

有多级指针,但是没有多级引用

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

引用比指针使用起来相对更安全,因为不用像指针那样要考虑野指针、空指针等问题。

总结

本文讲述了,C++中的引用&,下一篇将讲述C++初识下篇。

respect!

下篇见!

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多