【C++入门第二期】引用 和 内联函数 的使用方法及注意事项

news2024/11/18 17:46:29

  • 前言
  • 引用的概念
  • 初识引用
      • 区分引用和取地址
  • 引用与对象的关系
  • 引用的特性
  • 引用的使用场景
  • 传值和引用性能比较
  • 引用和指针的区别
  • 内联函数
  • 内联函数的概念
  • 内联函数的特性

前言

本文主要学习的是引用 及 内联含函数,其中的引用在实际使用中会异常舒适

引用的概念

概念:引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。

打个比方:你的名字叫张小三,你妈叫你小张,同学叫你小三,公司员工叫你张总。如上所述虽然你名字叫张小三,但大家却叫着你不一样的别名(小张/小三/张总)且你也知道他们是在叫你

初识引用

使用方法:
类型& 引用变量名(引用对象别名) = 引用对象;

如下变量 rival 引用了 ival
在这里插入图片描述

区分引用和取地址

注意:& 即可 取地址 也能 引用

区分方法:&左边 “为类型名”则为引用 ,&左边“不是类型名”则为取地址 (先记住后面会解释为什么)
在这里插入图片描述

引用与对象的关系

如下:rival 引用了对象vial ,打印出 rival 和 ivil 的值一致
在这里插入图片描述
但仅仅只是值一样吗?
下面我打印出 rival 和 ivil 的地址,两者地址也一致
在这里插入图片描述
经实践得出以下两个点:
1:引用和被引用对象存储的内容一致
2:引用和被引用对象的地址一致
在这里插入图片描述

总结:地址都一样所以他们访问的是同一块内存

再举个栗子:一开始我们给 00FEFA10( 开始的4字节)这块地址取名(变量名)为ival ,后面我们又给 00FEFA10( 开始的4字节)这块地址取个新名字 rval,且新老名字均可用。

提出问题:既然引用是给和被引用对象访问的是同一块内存,那其功能是否一致呢?

下图依然是 rival 引用了对象vial ,rival改变时 rvial 也改变 ,ival改变时 rvial 也改变。
这是肯定的因为两者地址都一样
在这里插入图片描述

引用的特性

  1. 引用在定义时必须初始化
    在这里插入图片描述
    引用等于是给某个变量取别名,所以必须明确(初始化)这个别名给谁

  2. 一个变量可以有多个引用
    在这里插入图片描述
    还是上面那个例子:你的名字叫张小三,你妈叫你小张,同学叫你小三,公司员工叫你张总。
    虽然这几个名字有所不宜但都是你

  3. 引用一旦引用一个实体,再不能引用其他实体
    在这里插入图片描述

可以看到当 pa 初始化为 a 的引用后,pa = b 时 b 只是把值赋值给了 pa
pa 和 a 的地址仍然一样,只是值发生了变化。由此可见 , 引用一旦初始化则引用对象就不会发生改变

4:当引用对象为const修饰对象时,引用也应为 const 修饰
在这里插入图片描述
当 a 为 const int 类型时,用 int 类型的 pa 引用 a 时会报错。原因是两者类型不匹配当int类型对象引用const int 对象时存在权力放大,这是不被编译器允许的,语法本身也不允许。

因为int 类型是变量 ,而 const int 是具有常量属性的。所以引用对象也应为 const int 类型 ;如下
在这里插入图片描述
4.1 但是如果被引用对象为 int 类型 ,引用可以为const int 类型
在这里插入图片描述
如上图被引用对象为 int 类型 ,引用可以为const int 类型,这种现象叫做权力缩小, 权力缩小是被编译器及语法所允许的。

就如下图,在C语言中当我们不想让指针通过解引用改变变量时,就会在类型前加const,这也是一种权力缩小
在这里插入图片描述
总结:
1. 引用在定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
4. 引用时权力只能同等(int 和 int )或 缩小,不能放大。但引用的权力缩小并不影响被引用对象

引用的使用场景

  1. 做参数
    对比使用指针做参数使用起来更简单哦
    在这里插入图片描述
  2. 做返回值
    在这里插入图片描述
    但是注意返回的引用对象出了其函数作用域必须还存在,接收其返回值的对象将能接收到,如果返回对象出了函数作用域就被销毁了只能用传值返回

传值和引用性能比较

在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此大量用值作为参数或者返回值类型,效率是比较低的尤其是当参数或者返回值类型非常大时,效率就更低。

如下是传值调用和传引用调用的测试代码

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

经测试当我们传的是 A 这样的结构体分别循环100000次时,传值调用和传引用调用相差巨大分别是759和2,两者相差300多倍
在这里插入图片描述
如下是传值和引用返回测试代码

#include <time.h>
struct A { int a[100000]; };
A a;//返回值
A Test1( )
{
	return a;
}
A& Test2( )
{
	return a;
}
void DateTest()
{
	size_t begin1 = clock();//计时用的
	for (size_t i = 0; i < 100000; ++i)//执行Test1函数次数
		Test1();// 以值作为函数参数
	size_t end1 = clock();
	size_t begin2 = clock();
	for (size_t i = 0; i < 100000; ++i)//执行Test2函数次数
		Test2();// 以引用作为函数参数
	size_t end2 = clock();
	// 分别计算两个函数运行结束后的时间
	cout << "Test1(A)-time:" << end1 - begin1 << endl;
	cout << "Test2(A&)-time:" << end2 - begin2 << endl;
}
int main()
{
	//test1();
	DateTest();
	return 0;
}

经测试当我们返回的是 A 这样类型的结构体分别循环100000次时,值返回和引用返回相差巨大分别是1549和2,两者相差700多倍
在这里插入图片描述
通过上述代码的比较,发现传值和引用在作为传参以及返回值类型上效率相差很大
我给定的测试数据量都比较大,方便让大家看出差距,具体可用上述代码自行测试哦;

引用和指针的区别

在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间
在这里插入图片描述
底层实现上实际是有空间的,因为引用是按照指针方式来实现的。同时我们来看下引用和指针的汇编代码对比
如下图可以看到,引用和指针的汇编代码一致
在这里插入图片描述
引用和指针的不同点:
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何
一个同类型实体
4. 没有NULL引用,但有NULL指针
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32
位平台下占4个字节)
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
7. 有多级指针,但是没有多级引用
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
9. 引用比指针使用起来相对更安全

内联函数

引出:
当我们某个函数需频繁调用时,函数栈帧建立会异常频繁导致代码效率下降
在这里插入图片描述
以前我们会把一些短小的函数写成宏来解决这一问题比如下图
在这里插入图片描述
但是宏也有缺点
1.不能调试
2.没有类型安全的检查
3.有些场景下非常复杂

而 C++ 针对这一问题设计出了内联函数 ,内联函数的关键字为 inline

inline int ADD(int x, int y)
{
	int ch = x + y;
	return ch;
}
int main()
{
	ADD(1,2);
	ADD(1, 2);
	return 0;
}

内联函数的概念

以inline修饰的函数叫做内联函数编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率。

查看设置
在 release 版本下,inline 内联函数会直接在调用部分展开但是 release 版本下其他版本优化的太多,不太好观察
debug 版本则需要自己设置一下编译器才能查看,否则不会展开因为debug不做代码优化
(vs2019)首先打开解决方案资源管理器,右击项目名称,选中属性并打开点击常规->调试信息格式,选择程序数据库
在这里插入图片描述
在 C/C++ 优化一栏,将内联函数扩展部分选中 ->只适用于 __inline (/Ob1)
在这里插入图片描述
设置完成点击应用

设置前汇编代码,可以看到需要调用ADD函数
在这里插入图片描述
设置后汇编代码可以看到,call ADD不见了,call 是函数调用的指令,没有说明代码没有进行函数调用,内联函数直接在局部展开了。
在这里插入图片描述

内联函数的特性

  1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
    行效率。
  2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。下图为《C++prime》第五版关于inline的建议:
    在这里插入图片描述
  3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

结论:函数内容较短,且频繁调用的函数建议定义成 inline 可节省函数栈帧的消耗

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/370362.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于SpringBoot的企业资产管理系统

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…

蓝桥2.24训练

1&#xff0c;奇怪的函数 P2759 奇怪的函数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 1这道题有两个点&#xff0c;一个是求数的位数 2&#xff0c;用整数二分求出的位数与n比较 #include <bits/stdc.h> using namespace std; typedef long long ll; ll n; int ma…

【华为OD机试模拟题】用 C++ 实现 - 矩阵最值(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

flutter- JSON解析框架使用方法json_serializable

对于目前来说&#xff0c;大部分的API网络请求的通讯内容数据格式都是JSON。JSON返回的都是字符串&#xff0c;假如要取到data里面的id&#xff0c;去直接字符串截取肯定是不行的&#xff0c;要通过一定的方式把它解析成Map或者解析成对象&#xff0c;再去处理它。像一些简单的…

YOLOv5深度剖析

目录 YOLOv5算法概述 网络结构 backbone Conv模块&#xff1a; C3模块 SPPF neck 图形特征与语义特征 特征金字塔 head YOLOv5算法原理 以感受野理解网格 框回归以及分类 YOLOv5细节 消除网格敏感度 b. 正样本匹配 c. 损失计算 YOLOv5算法概述 YOLOv5与历代YO…

【异常】导出Excel异常This archive contains unclosed entries.

一、异常说明 二、定位问题代码 一看问题, 上下文都是与订单相关的内容。 查询代码的使用地方,发现出现在这个Mybatis的select语句中 查看备注,发现是订单物流,那就没跑了, 肯定是商城的物流模块出了问题 那是什么地方用到了这个物流模块而且涉及到导出功能呢? 经验告诉…

代码随想录算法训练营day42 |动态规划之背包问题 11.分割等和子集 1049. 最后一块石头的重量 II 494. 目标和 474.一和零

day4211.分割等和子集1.dp数组的含义2.确定递推公式3.dp数组初始化4.确定遍历顺序5.打印dp数组1049. 最后一块石头的重量 II1.确定dp数组以及下标的含义2.确定递推公式3.dp数组如何初始化4.确定遍历顺序5.举例推导dp数组494. 目标和1.确定dp数组以及下标的含义2.确定递推公式3.…

ChatGPT懂SAP吗?跟ChatGPT聊完后,我暂时不担心会失业

我弄了个ChatGPT账号。随便问了一下他SAP的问题。贴出来跟大家分享一下。 问题1&#xff1a;我是 SAP 开发工程师&#xff0c;你有什么可以帮到我。 作为一个语言模型&#xff0c;我可以为您提供关于SAP开发的一般性建议&#xff0c;以及关于如何使用SAP软件的一些技巧。以下是…

【华为OD机试模拟题】用 C++ 实现 - 合规数组(2023.Q1)

最近更新的博客 华为OD机试 - 入栈出栈(C++) | 附带编码思路 【2023】 华为OD机试 - 箱子之形摆放(C++) | 附带编码思路 【2023】 华为OD机试 - 简易内存池 2(C++) | 附带编码思路 【2023】 华为OD机试 - 第 N 个排列(C++) | 附带编码思路 【2023】 华为OD机试 - 考古…

流感来了,这类人最容易感染!

最近有学校因多名学生发热停课&#xff0c;浙江多地疾控也提醒大家现在是进入了甲流高发期。今天就来讲一讲甲流该如何防护。首先甲流与普通感冒不同&#xff0c;感冒病原体是鼻病毒、冠状病毒、副流感病毒等。流感病毒是正粘病毒科&#xff0c;根据核蛋白和基质蛋白M1抗原性的…

六千字让你明白什么是数字孪生?

文章目录1. 背景2. 数字孪生基础2.1 概念2.2 价值3. 技术生态3.1 技术体系3.2 核心技术3.2.1 多领域、多尺度融合建模3.2.2 数据驱动与物理模型融合的状态评估3.2.3 数据采集和传输3.2.4 全生命周期数据管理3.2.5 虚拟现实呈现3.2.6 高性能计算3.3 建设3.3.1 重点3.3.1.1 数字孪…

3款强大且实用的电脑软件,颠覆你的认知,值得一试

闲话少说&#xff0c;直上狠货。 1、一个木函 一个木函仅一张照片的体积&#xff0c;却提供了与日常、图片、设备、文件、文字处理等等相关的80多种工具&#xff0c;相当实用&#xff0c;更牛的是&#xff0c;完全免费&#xff0c;无任何弹屏广告。一个木函体积小&#xff0c;简…

力扣-游戏玩法分析

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;511. 游戏玩法分析二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.其他总结…

【Acwing 周赛复盘】第91场周赛复盘(2023.2.18)

【Acwing 周赛复盘】第91场周赛复盘&#xff08;2023.2.18&#xff09; 周赛复盘 ✍️ 本周个人排名&#xff1a;1286/3115 AC情况&#xff1a;2/3 这是博主参加的第六次周赛&#xff0c;周赛当晚有事&#xff0c;是后来定时自测的 &#x1f602; 在 20 分钟内 AC 了 2 题&…

计算机408考研先导课---C语言难点2

目录 一、字符型数据与字符串型数据的比较 1、字符型数据特点 2、字符串型数据特点 二、字符数组 1、定义 2、输入输出 ①输入 ②输出 3、字符处理函数 ①put函数 ②gets函数 ③strcat函数 ④strcpy函数 ⑤strcmp函数 ⑥strlen函数 ⑦strlwr函数 ⑧strup…

LeetCode练习三:链表

文章目录一、链表基础1.1 无序表&#xff08;UnorderedList&#xff09;1.1.2 双向链表1.1.3 循环链表1.2 链表的基本操作1.2.1 定义链表结构1.2.2 建立线性链表1.2.3 求线性链表的长度1.2.4 查找元素1.2.5 插入元素1.2.6 改变元素1.2.7 删除元素1.3 有序表OrderedList1.4 链表…

Redis之消息队列实现

文章目录秒杀场景采用消息队列实现List实现消息队列PubSub&#xff08;发布订阅&#xff09;实现消息队列基于Stream实现消息队列消费者组实践总结秒杀问题是非常重要且比较难实现的&#xff0c;如果不进行架构的优化的话&#xff0c;直接访问会给业务系统造成很大的压力… 秒杀…

【Linux】system V共享内存 | 消息队列 | 信号量

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;system V共…

go进阶(2) -深入理解Channel实现原理

Go的并发模型已经在https://guisu.blog.csdn.net/article/details/129107148 详细说明。 1、channel使用详解 1、channel概述 Go的CSP并发模型&#xff0c;是通过goroutine和channel来实现的。 channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲&#xf…

通用信息抽取技术UIE产业案例解析,Prompt 范式落地经验分享!

想了解用户的评价究竟是“真心夸赞”还是“阴阳怪气”&#xff1f;想快速从多角色多事件的繁杂信息中剥茧抽丝提取核心内容&#xff1f;想通过聚合相似事件准确地归纳出特征标签&#xff1f;……想了解UIE技术在产业中的实战落地经验&#xff1f;通用信息抽取技术 UIE 产业案例…