深度解读《深度探索C++对象模型》之C++的临时对象(二)

目录

临时对象的生命期

特殊的情况


接下来我将持续更新“深度解读《深度探索C++对象模型》”系列,敬请期待,欢迎左下角点击关注!也可以关注公众号:iShare爱分享,或文章末尾扫描二维码,自动获得推文和全部的文章列表。

上一篇中讲解了C++编译器在什么情况下会产生临时对象,上一篇请从这里阅读:

深度解读《深度探索C++对象模型》之C++的临时对象(一) 

这一篇来讲解临时对象的生命周期,上篇讲解何时产生临时对象,这篇讲解临时对象存在的时间以及何时会被销毁。

临时对象的生命期

        C++标准里规定:为某表达式创建了一个临时对象,则此临时对象将一直存在直到包含有该表达式的最大的表达式计算完成为止。怎么理解这句话?我们以一行代码来解释,如下的代码:

// 假设verbose为bool类型变量,根据这个开关输出不同的信息
// a,b,c,d皆为string类型的对象。
verbose ? a + b : c + d;

        上面的表达式即是一个完整的表达式,也是一个最大的表达式,里面包含三个子表达式:“verbose”,“a + b”,“ c + d”,其中“a + b”和“c + d”两个子表达式将会有可能产生临时对象,视verbose的测试结果将运算不同的子表达式。这个运算的中间过程产生的临时对象需要等到完整的表达式运算完成之后才能被销毁。我们还是以上面的测试代码为例,修改下main函数,其它代码不变,如下:

int main() {Object a;Object b;printf("a + b = %d\n", (int)(a + b));return 0;
}

        它生成的对应的汇编代码:

main:                                   # @main# 略...lea     rdi, [rbp - 8]call    Object::Object() [base object constructor]lea     rdi, [rbp - 16]call    Object::Object() [base object constructor]lea     rdi, [rbp - 32]lea     rsi, [rbp - 8]lea     rdx, [rbp - 16]call    operator+(Object const&, Object const&)lea     rdi, [rbp - 32]call    Object::operator int()# 略...call    printf@PLTlea     rdi, [rbp - 32]call    Object::~Object() [base object destructor]

        [rbp - 8]是对象a,[rbp - 16]是对象b,[rbp - 32]是临时对象。上面代码的第14行在调用printf函数之后才去调用了Object类的析构函数去析构临时对象。

        但当临时对象是根据程序的测试语句有条件的被产生出来时,临时对象的生命期规则就变得复杂了。还是以上面的例子,修改如下:

int main() {Object a;Object b;Object c;Object d;if ( a + b || c + d) { printf("Go here.\n"); }return 0;
}

        第6行的if语句中,子表达式“c + d”是否会被运算取决于前面的子表达式“a + b”的测试结果,只有它的结果为false的时候才会评估子表达式“c + d”,这时候才会产生一个临时对象。临时对象应该要销毁,但不是无条件的销毁,要根据它是否有被产生出来而决定。上面的if语句大概可以转换成以下的语句:

// 伪代码:
Object tmp1 = a + b;
int test1 = tmp1.operator int();
int test2 = 0;
if (test1 == 0) {Object tmp2 = c + d;test2 = tmp2.operator int();// 此处销毁临时对象tmp2?(1)
}
tmp1.Object::~Object();
// 此处销毁临时对象tmp2?(2)

        临时对象tmp2应该在哪里被销毁,在(1)处还是在(2)处?其实这两种处理方式都不准确,首先是在(1)处时还不能销毁,根据C++的标准规定,这时完整的表达式还没有运算完,此时还不能销毁临时对象。其次是(2)处的处理也不妥当,因为有可能tmp2并未被产生出来,它需要根据条件来决定是否销毁。来看看编译器是怎么做的,看看这行代码对应的汇编:

main:                                   # @main# 省略构造的代码,对象a,b,c,d的存放位置分别是:  #[rbp - 8],[rbp - 16],[rbp - 32],[rbp - 40]mov     byte ptr [rbp - 57], 0# 省略a+b的代码,产生的临时对象存放在[rbp - 48]lea     rdi, [rbp - 48]call    Object::operator int()mov     dword ptr [rbp - 64], eax       # 4-byte Spillmov     ecx, dword ptr [rbp - 64]       # 4-byte Reloadmov     al, 1cmp     ecx, 0mov     byte ptr [rbp - 65], al         # 1-byte Spilljne     .LBB3_9# 省略c+d的代码,产生的临时对象存放在[rbp - 56]mov     byte ptr [rbp - 57], 1lea     rdi, [rbp - 56]call    Object::operator int()mov     dword ptr [rbp - 72], eax       # 4-byte Spillmov     eax, dword ptr [rbp - 72]       # 4-byte Reloadcmp     eax, 0setne   almov     byte ptr [rbp - 65], al         # 1-byte Spill
.LBB3_9:mov     al, byte ptr [rbp - 65]         # 1-byte Reloadmov     byte ptr [rbp - 73], al         # 1-byte Spilltest    byte ptr [rbp - 57], 1jne     .LBB3_10jmp     .LBB3_11
.LBB3_10:lea     rdi, [rbp - 56]call    Object::~Object() [base object destructor]
.LBB3_11:lea     rdi, [rbp - 48]call    Object::~Object() [base object destructor]mov     al, byte ptr [rbp - 73]         # 1-byte Reloadtest    al, 1

        编译器是以一个标志位来标记是否有临时对象的产生,见上面代码的第4行,标志位存放在[rbp - 57]中,占用一个byte的大小,默认值设置为0。

        然后接下来的第5到第13行包括省略掉的代码,是运算“a + b”然后对其转换成int型数据再进行测试,这里“a + b”的运算过程产生临时对象存放在[rbp - 48]。第10行的al保存的是条件测试的结果,也就是决定执行if后面的语句还是执行else后面的语句的作用,它也是一个byte大小,暂存在[rbp - 65]中。

        第13行就是根据“a + b”的结果是否继续评估后续的“c + d”,如果为0将继续评估“c + d”,第14行到第20行包括省略的代码就是运算“c + d”,这里将产生临时对象,存放在[rbp - 56]中,同时最主要的一点就是将记录是否产生临时对象的标志位设置为1,即代码的第15行,[rbp - 57]现在的值为1。第21行的setne指令的作用是取标志寄存器中ZF的值并取反后,再放到AL中,这个是决定整个if语句括号中的测试条件的真假。

        接下来从标签.LBB3_9开始的代码就是根据if语句里的条件测试结果决定是执行if语句还是else语句,以及根据标志位是否需要销毁临时对象。第26行就是测试[rbp - 57]的值是否为1,根据上面的分析,如果有评估到“c + d”这里的话就会产生临时对象并这个值被设置为1,这里判断为1的话就跳转到.LBB3_10标签处执行析构动作,如果为0则跳过这段代码。接着就是销毁“a + b”产生的临时对象(存放在[rbp - 48]),这个临时对象是一定会产生的,所以不必判断标志位,然后第36行就是根据if语句的测试结果决定接下来的代码流程。

特殊的情况

        上面提到的临时对象在完整的表达式运算完之后就会被销毁掉,但有两个例外的地方,它不会马上被销毁,而是会继续存在一段时间,例如:

  • 表达式被用来初始化一个对象时

        如下面的代码:

bool condition;
Object obj = condition ? a + b : c + d;

        其中“a + b”和“c + d”将根据测试结果产生出临时对象,根据规则临时对象在“?:”这个完整表达式结束后就可以被销毁,但这时需要用这个临时对象来初始化obj对象,所以不能马上销毁它,必须要等到初始化完obj之后才能销毁。

  • 当临时对象被一个引用绑定时

        如下面的代码:

const Object &ref = a + b;

        引用ref将绑定到一个“a + b”产生的临时对象上,编译器将它转换为如下的伪代码:

Object tmp = a + b;
const Object &ref = tmp;

        这种情况下,临时对象tmp不能被释放,否则ref将引用到一个空对象,临时对象将会一直被保留,直到绑定到它的引用ref的生命周期结束。


本主页会定期更新,为了能够及时获得更新,敬请关注我:点击左下角的关注。也可以关注公众号:请在微信上搜索公众号“iShare爱分享”并关注,或者扫描以下公众号二维码关注,以便在内容更新时直接向您推送。

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

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

相关文章

VTK数据的读写--Vtk学习记录1--《VTK图形图像开发进阶》

读和写操作是VTK可视化管线两端相关的类--Reader和Writer类 Reader:将外部数据读入可视化管线,主要步骤如下 s1:实例化Reader对象 s2:指定所要读取的文件名 s3:调用Update()促使管线执行 对应的Writer: s1:实例化Writer对象 s2输入要写的数据以及指定写入的文…

如何实现网页上3D模型的展示、浏览和互动?

实现网页上3D模型的展示、浏览和互动,可以通过以下步骤进行: 1、创建3D内容:使用3ds max、Maya、blender、C4D等3D软件制作好3D模型。 2、设计3D应用:把制作好的模型导出为fbx、obj、dae、gltf、glb等格式文件,上传到…

^_^填坑备忘^_^C#自动化编程实现STK+Exata对卫星互联网星座进行网络仿真

C#实际选择 STK11版本 or STK12版本的问题备注。 【C#自动化客户端调用STK时,实际选择 STK11版本 or STK12版本 的调试运行备注】 以下代码“更新并重新打包备份为”〔testSTKQualNetInterface备份08.1_★避坑★【种子卫星:天线直接安装在卫星上&#…

check startup检查各种资源文件

check startup 命令功能 check startup命令用来检查各种资源文件(paf文件、补丁包、启动软件、配置文件)是否正确。 命令格式 check startup [ crc ] [ next ] 参数说明 参数参数说明取值 crc 对资源文件进行CRC校验。 - next 检查下一次启动的各…

scitb5函数2.1版本(交互效应函数P for interaction)发布----用于一键生成交互效应表、森林图

写在前面的话,此函数不适用于NHANES数据,也不能用于COX回归,请注意甄别。 在SCI文章中,交互效应表格(通常是表五)几乎是高分SCI必有。因为增加了亚组人群分析,增加了文章的可信度,能为文章锦上添…

规培报名身份证上传怎么小于500k?这几个方法试试看

大家都知道在规培报名的时候,是需要上传一些自己的个人信息资料到平台上的,其中身份证照片是比较重要的一项,我们自己拍的身份证照片大小有时候可能不符合网站的规定,需要去做一些图片修改调整,比如图片你压缩&#xf…

OpenAI潜入黑客群聊!盗用ChatGPT被换成“喵喵GPT”,网友:绝对的传奇

当ChatGPT被**黑客“入侵”**时,OpenAI会如何应对? 掐断API,不让他们用?不不不。 这帮极客们采取的做法可谓是剑走偏锋——反手一记《无间道》。 故事是这样的。 OpenAI虽然在发布ChatGPT之前做了大量的安全性检测,…

基于FPGA的数字信号处理(10)--定点数的舍入模式(1)四舍五入round

1、前言 将浮点数定量化为定点数时,有一个避不开的问题:某些小数是无法用有限个数的2进制数来表示的。比如: 0.5(D) 0.1(B) 0.1(D) 0.0001100110011001~~~~(B) 可以看到0.5是可以精准表示的,但是0.1却不行。原因是整数是离散的…

物流EDI:GEFCO EDI 需求分析

GEFCO专注于汽车物流领域近70年,是欧洲整车市场的物流供应商,也是欧洲十大领先的运输和物流集团之一。GEFCO的业务遍及六大洲,业务覆盖150个国家,在全球拥有庞大的员工队伍,在全球汽车行业的挑战中茁壮成长。为汽车制造…

如何通过 4 种方式在 Mac 上恢复未保存的 Excel 文件

您曾在 MacBook 上花费数小时处理 Excel 工作簿,但现在它消失了。或者,当您退出 Excel 文件时,您无意中选择了“不保存”。这是不是说你所有的努力都白费了?本文系统地介绍了如何在 Mac 上恢复丢失的 Excel 文件。通过我们的 4 种…

响应式编程Spring Reactor探索

一,介绍 响应式编程(Reactive Programming),简单来说是一种生产者只负责生成并发出数据/事件,消费者来监听并负责定义如何处理数据/事件的变化传递方式的编程思想。 响应式编程借鉴了Reactor设计模式,我们…

孩子用什么样的灯对眼睛没有伤害?分享多款满分护眼台灯

为人父母以后,深感压力山大。如今不仅要抓孩子的学习,还得时刻关注孩子的身心健康,尤其是视力问题。现在不少学生都存在近视的现象,而导致这一现象的主要原因,除了平时的学业压力过大以外,夜晚学习的光线也…

第二证券|为什么指数涨回来了钱没回来?

在a股市场上,常常会呈现指数涨回来了钱没回来的状况,呈现这种状况的原因如下: 1、大盘上涨是权重股所造成的 大盘上涨或许是受一些权重比较大的职业所影响,比方证券职业、钢铁职业、银行职业等等,这些职业的大涨&…

Progesterone(孕酮/黄体酮) ELISA检测试剂盒--3小时内可得到检测结果

孕酮(Progesterone)又称为黄体酮,是卵巢分泌的具有生物活性的主要孕激素,负责与生殖有关的活动,如乳房腺体发育、参与月经周期以及妊娠的建立和维持。此外,孕酮还参与支持妊娠期间的生理过程,包…

致远M3 log 敏感信息泄露漏洞

文章目录 免责漏洞描述漏洞原理影响版本漏洞复现修复方法 免责 只为学习与交流,若利用做一切违法乱纪的事与本人无关 漏洞描述 致远M3是一个企业移动业务管理平台,全面覆盖各种工作场景,通过智能化的办公和业务场景融合,为企业…

Cloudera的简介及安装部署

简介 Cloudera是一家位于美国的软件公司,成立于2008年,专注于为企业客户提供基于Apache Hadoop的软件、支持、服务以及培训。Cloudera的开源Apache Hadoop发行版,即Cloudera Distribution including Apache Hadoop(CDH&am…

Linux中的软连接和硬链接

一、软和硬链接连接 在Linux系统中,软连接(符号链接)和硬链接是文件系统中两种不同类型的链接,它们用于创建对文件的引用。下面详细解释这两种链接的特点和区别:、 软连接(符号链接) 定义&…

项目解决方案:多台poe摄像机接到3台NVR上,如何进行统一管理

目录 一、概述 二、建设目标及需求 三、设计依据与设计原则 1、先进性与适用性 2、经济性与实用性 3、可靠性与安全性 4、开放性 5、可扩充性 6、追求最优化的系统设备配置 7、提高监管力度与综合管理水平 四、建设方案设计 (一)系统方案设计…

旋转矩阵(将坐标轴旋转)

旋转矩阵(将坐标轴旋转) 在二维空间中,旋转可以用一个单一的角 定义。作为约定,正角表示逆时针旋转。把笛卡尔坐标的列向量关于原点逆时针旋转的矩阵是: 原坐标系下 坐标系逆时针旋转β 补充 sin(-a) -sin(a) co…

超越Scratch的梦 用心打造商业系统图形编程体验

在一个阳光明媚的上午,卧龙和凤雏正在公司会议室激烈地讨论着图形化编程产品在商业系统开发中的应用和改进。会议室里摆放着一些电脑和投影仪,方便他们展示和演示相关的内容。 “你知道图形化编程在商业系统开发中没有被广泛应用的原因吗?”卧…