文章目录

  • 1、引用的概念
  • 2、引用的价值
    • 2.1 做参数
    • 2.1 做返回值
  • 3、引用的意义
    • 3.1 传引用做参数的意义
    • 3.2 传引用返回值的意义
  • 4、引用和const
    • 4.1 引用的权限
    • 4.2 const 引用修饰常量
  • 5、引用和指针区别

1、引用的概念

  引用不是定义一个新的变量,而是给已有的变量定义一个别名,编译器不会给引用开辟内存空间,它和它引用的变量共用一块空间。

类型& 引用变量名(对象名) = 引用实体;

比如:

int a = 10;
int& ai = a;
//正如一个人可以取多个外号,实体也可以有多个引用。
int& aii = a;

ai 就是 a的别名,ai 是 a的另一个称号。

所以有很明显的几个规则:

  1. 得先有本体,再有别名,所以引用不能空引用,且引用必须有初始值
  2. ai作为a的别名后,不能再作为其它的别名,不然会产生歧义。
  3. 如果改变ai的值,那么a的值也会改变,因为它们都是同一个。
  4. 正如一个人可以取多个外号,实体也可以有多个引用。
  5. 引用类型必须和引用实体是同种类型的

2、引用的价值

2.1 做参数

看代码:
输出型参数

可以有效避免实参的拷贝。

void swap(int& x, int& y)
{int temp = x;x = y;y = temp;
}
//一些OJ题中,参数返回值
int* func(int* a, int size, &returnSize)
{}

有效避免二级指针的使用

typedef struct ListNode
{struct ListNode* next;int val;
}LTNode, *PLTNode;//struct ListNode*& phead
void SlistPushBack(PLTNode& phead, int x)
{}

可见,有些指针能实现的地方,引用也能实现。

2.1 做返回值

引用的正确使用:

传值返回
先看一串代码:

int Count()
{static int n = 0;n++;return n;
}int main()
{int ret = Count();return 0;
}

要想清晰的理解这段代码,我们得借助函数栈帧知识。

在图中栈帧往下开辟,静态变量n存储在静态区,Count函数在返回后销毁,销毁之前会为一个临时变量开辟空间,返回的n=1作为一个临时变量放在寄存器中(数据较小将放入CPU的寄存器中,如果数据较大就会在上一个栈帧中开好空间存放),再将寄存器的内容返回给ret。

为什么要返回临时变量?
因为如果是单单普通的Int n 返回,函数中销毁后n也销毁,获取的n也就没有意义了。

引用返回

int& Count()
{static int n = 0;n++;return n;
}int main()
{int ret = Count();return 0;
}

Count栈帧销毁后,由于n存储在静态区,直接返回n的引用,这一过程虽然产生了临时变量但是返回引用所以不开辟空间。


引用的错误使用:

传值返回(这是正确的)

int Count()
{int n = 0;n++;return n;
}int main()
{int ret = Count();return 0;
}

与之前传值相比,n的值存放在栈区随着Count函数销毁也会被销毁,但销毁之前,也会传一个临时变量给ret,这个临时变量开辟空间。

注意:这里的销毁指的不是开辟Count的那块区域被销毁了,而是依然能访问也能修改,只是非法的,并且那块区域的数据已经不受保护了,读取的数据都是不确定的,相当酒店退房一样。

引用返回
这是一个错误示范

int& Count()
{int n = 0;n++;return n;
}int main()
{int ret = Count(); //非法访问return 0;
}

由于引用返回产生的临时变量不开辟空间,那么在n销毁后,返回的n的引用也就构成了非法访问。

总结:

  1. 出了函数作用域,返回变量不存在了,不能用引用返回,因为引用返回的结果都是未定义的。
  2. 出了函数作用域,返回变量存在,才能用引用返回。

3、引用的意义

3.1 传引用做参数的意义

减少拷贝,提高效率,能修改参数
对函数传参,并不会对函数直接传实参,而是会传递一个临时拷贝的形参,尤其当数据量大时,造成效率低下。通过传引用不需要拷贝,很好的解决效率问题。

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{A a;// 以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 1000000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 1000000; ++i)TestFunc2(a);size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

4w字节的数据,100w次的调用。

3.2 传引用返回值的意义


减少拷贝,提高效率,能修改参数
由于函数在返回值的时候,需要返回一个开辟空间的临时变量,在数据量足够多的时候,效率是比较低下的,而返回引用值的时候,返回的临时变量不需要开辟内存空间,这就提高了效率,但是得正确使用引用

#include <time.h>
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 < 100000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl; //cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

当数据量或返回次数越多,这个差距也会越大。

4、引用和const

4.1 引用的权限

const使得变量不能修改,使得变量只有只读权限,如果引用该变量,那么对应的引用类型也必须带上const,权限也只有只读。(权限不能放大)

如果变量有读和写的权限,那么引用的权限可以只读。(权限的缩小)

int main()
{int a = 0;// 权限平移int& ra = a;// 指针和引用赋值中,权限可以缩小,但是不能放大。const int b = 1;//拷贝a = b;//权限放大不行//int& rb = b;//权限缩小可以const int& rra = a;//rra++   //erra++;  //改变rra和areturn 0;}

const引用避免传参的限制
看代码:
因为权限的问题,在对参数是引用的函数进行传参,需要考虑实参权限是否可以对应。
为了在多种场景下都能使用该函数,许多的函数实现在设计引用参数时都加上了const起到保证作用。

//void Func(int& x){}void Func(const int& x){}int main()
{int a = 0;// 权限平移int& ra = a;// 指针和引用赋值中,权限可以缩小,但是不能放大。const int b = 1;//拷贝a = b;//权限放大不行//int& rb = b;//权限缩小可以const int& rra = a;//权限平移const int& rb = b;Func(a);Func(ra);Func(rra);Func(b);Func(10); //下一节会提到为什么可以return 0;}

以上的函数调用都能在func(const int&x)中成功。

并且上面两个函数构成函数重载。

4.2 const 引用修饰常量


const 引用可以初始化引用常量

const int& b = 10;

所以之前Func(10)为什么可以调用,也就解决了。

截断、强转和整型提升都会产生临时变量

 double d = 12.34;cout << (int)d << endl;//打印临时变量int i = d; //(截断) 实际临时变量12赋予给了i


临时变量有常量的特性

int Count()
{int n = 0;n++;return n;
}int main()
{const int& b = 10;double d = 12.34;//int& ri = d; //不行const int& ri = d; //引用临时变量 临时变量具有常性const int& ret = Count(); //函数返回临时变量
}

5、引用和指针区别

语法概念上引用和本体共用一块空间,指针需要开辟一块内存存储地址。
底层实现中,有空间的,它们实现都是一样的。本质引用和地址都是传地址,只是引用是由编译器做。

int main()
{int a = 10;int& ra = a;ra = 20;int* pa = &a;*pa = 20;return 0;
}


具体不同点:

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

  2. 引用在定义时必须初始化,指针没有要求

  3. 没有NULL引用,但有NULL指针

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

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

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

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

  8. 引用比指针使用起来相对更安全,

【C++入门】烦人的引用相关推荐

  1. qdialog不允许放大缩小弹窗_iOS 13系统太烦人!频繁弹窗提醒App正在定位,竟是为保护隐私?...

    近日,针对iOS 13系统用户频繁收到App追踪消息的弹窗提醒这一问题,苹果回应称,此项更新功能旨在保护用户的隐私,避免被App在后台窃取用户的位置信息并追踪. 据相关媒体报道称,有些开发者担心此项定 ...

  2. Vue CLI 3开发中屏蔽烦人的EsLint错误

    问题 Vue开发中,特别是当你阅读分析别人的其中早期版本的Vue代码时往往会遭遇到满屏幕的烦人的EsLint错误.有关EsLint这个工具的作用不再赘述.查阅网上参考文档,大多是针对早起版本Vue C ...

  3. java scala_经过几天的Scala回归Java的10个最烦人的事情

    java scala 因此,我正在尝试使用Scala,因为我想编写一个解析器,而Scala Parsers API似乎非常合适. 毕竟,我可以在Scala中实现解析器并将其包装在Java接口后面,因此 ...

  4. 经过几天的Scala回归Java的10个最烦人的事情

    因此,我正在尝试使用Scala,因为我想编写一个解析器,而Scala Parsers API似乎非常合适. 毕竟,我可以在Scala中实现解析器并将其包装在Java接口后面,因此除了附加的运行时依赖关 ...

  5. 从C快速入门C++ (命名空间、引用、函数重载)

    从C快速入门C++ (命名空间.引用.函数重载) C++关键字 const const 的存储位置 结论: 命名空间 C++预处理器 和 iostream 文件 头文件名 命名空间使用 函数重载 默认 ...

  6. cad中直径符号不显示_CAD导图中常见的4个烦人问题,解决方案都在这里,还不收藏一波?...

    问题1:CAD图纸导入后显示过小? 这是因为CAD图纸中还有其他的图元,它可能是一个很小的点或是很短的线条,只有在CAD中全屏查看,才能看见.把它删了就可以让CAD图纸在导入之后正常显示了. 具体做法 ...

  7. 广告太多超烦人?让你和烦人的弹窗广告说拜拜!

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 每天凌晨00点00分, 第一时间与你相约 每日英文 Women must be able to deal with lies, ...

  8. 安卓网页广告拦截_拦截烦人的网页广告,增加上网体验

    本号所有资源版权归原作者所有,如有侵权请加小编微信删除.本号免费分享,仅供学习交流,下载后24小时内请自觉删除,切勿用于商业用途,否则后果自负! 今天重新整理分享几大主流浏览器和IE浏览器拦截广告的方 ...

  9. Jmeter之解决烦人的中文乱码问题

    Jmeter之解决烦人的中文乱码问题 参考文章: (1)Jmeter之解决烦人的中文乱码问题 (2)https://www.cnblogs.com/liulinghua90/p/6973289.htm ...

最新文章

  1. 文件名有规则情况读取
  2. 美团点评SQL优化工具SQLAdvisor开源
  3. .html天气预报上蔡,上蔡天气预报15天
  4. Cisco配置单臂路由及静态路由
  5. .net 5 正式版_ASP.NET Core 3时代全新开启,DevExpress ASP.NET增强Data Grid
  6. 经典面试题(19):以下代码将输出的结果是什么?
  7. java sbyte_JAVA与c#中byte取值范围的差异
  8. pod install 在1.0.0.beta.1 cocoapods版本显示错误
  9. 打破超星封锁——PDG转PDF方法
  10. 北京师范大学c语言题库,北京师范大学C语言题库.doc
  11. 基于Layabox引擎的魔塔微信小游戏设计与实现
  12. 关于VO、RVO、ORCA的个人理解
  13. webstorm连接github上传代码
  14. 大数据项目之电商数仓(业务数据仓库)
  15. 文献简读——大肠中胰蛋白酶降解共生菌的鉴定【Identification of trypsin-degrading commensals in the large intestine】
  16. 一文带你了解800万像素车载摄像头
  17. 计算机为什么找不到摄像头,为何电脑里找不到摄像头设备?
  18. Struts2动作类:Action
  19. flink source 同步_网易云音乐基于 Flink + Kafka 的实时数仓建设实践
  20. oracle中raw是什么格式,oracle中RAW数据类型

热门文章

  1. 每日一题(2022-05-04)——找出游戏的获胜者
  2. 什么是事务(对事物的简单介绍)
  3. html5无序列表,有序列表,定义列表,组合标签,div分区标签
  4. HBase命令行基本操作
  5. c语言程序设计迷宫问题,C语言迷宫问题
  6. 转载一篇人生思考:一名大学毕业生的反思
  7. R语言多元线性回归模型分析 习题
  8. 0082-莱布尼兹三角形
  9. MySQL学习笔记——数据库的创建、修改与删除
  10. Linux内核源代码下载