C++对象拷贝时的优化编译

在现代编译器中,当我们在 C++中进行对象的拷贝操作时,编译器并非只是机械地执行逐字节的复制。相反,它会进行优化,避免不必要的拷贝构造等等,这种优化包括“返回值优化”(RVO),“拷贝省略” 等等。以下代码运行结果是在vs2022下实现的。

#include<iostream>
using namespace std;class A
{
public:A(int a = 0):_a1(a){cout << "A(int a)" << endl;}//拷贝构造A(const A& aa):_a1(aa._a1){cout << "A(const A& aa)" << endl;}A& operator=(const A& aa){cout << "A& operator=(const A& aa)" << endl;if (this != &aa){_a1 = aa._a1;}return *this;}//析构~A(){cout << "~A()" << endl;}
private:int _a1 = 1;
};
void f1(A aa)
{}
A f2()
{A aa;return aa;
}

1.内置类型转换为自定义类型的优化

int main()
{A aa = 1;return 0;
}

    在语法上来看,也就是没有优化的情况下,(1)编译器先调用默认构造函数,在栈上创建临时对象,传入参数值1初始化临时对象的成员变量,(2)再将这个临时对象去拷贝构造给aa,(3)aa被初始化成功后析构这个临时对象,(4)等到程序结束时再析构aa

    从下面的结果来看,也就是优化之后,并没有调用拷贝构造,(1)编译器可能直接调用默认构造函数初始化aa,而不创建临时对象和调用拷贝参数,(2)然后在程序结束时析构aa。

2.传值传参时的优化

void f1(A aa)
{}
int main()
{A aa1;f1(aa1);retrun 0;
}

从语法上看,(1)当执行A aa1时,编译器会调用A类的默认构造函数。由于没有提供参数,默认构造函数A(int n = 0)会被调用,_a1被初始化为0。(2)随后执行f1(aa1),编译器会调用A类拷贝构造函数将aa1的值拷贝到临时对象aa中,(3)然后进入函数f1, 该函数结束时调用析构函数析构aa,(4)最后main函数结束时析构aa1。从结果上来看,此处没有优化。

3.隐式类型

void f1(A aa)
{}
int main()
{	//隐式类型,连续构造+拷⻉构造->优化为直接构造f1(1);return 0;
}

从语法上来看,(1)当调用f1(1)时,编译器调用A的默认构造函数将整数1隐式转换为A类对象(2)进入函数f1,由于f1的参数是按值传递的,A类的临时对象aa被创建,并且刚刚创建的对象会被拷贝构造到临时对象aa中,(3)当函数f1结束时,析构aa。当main函数结束时,此处由于没有其它对象,所以没有再调用析构函数。

优化过之后,(1)编辑器可能直接会直接在f1的参数位置调用默认构造函数,使用参数1进行构造,(2)而不是先创建一个临时对象再拷贝,因此没有拷贝构造。(3)在f1内部,aa直接引用构造函数,到函数f1结束时,析构aa。得到了下面的结果                                                                                                                                                   

4.传值返回

A f2()
{A aa;return aa;
}
int main()
{// 传值返回// 返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019 debug)// ⼀些编译器会优化得更厉害,进⾏跨⾏合并优化,直接变为构造。(vs2022 debug)//构造+拷贝构造-》直接构造A aa2 = f2();
}

从语法上来看,(1)当调用A aa2 = f2();时,程序进入f2函数,首先创建局部对象aa,调用默认构造函数初始化aa,(2)返回时会触发拷贝构造函数,创建一个临时对象用于返回值传递,(3)在main函数中临时对象初始化aa2再次触发拷贝构造,(4)当f2函数执行完毕,编译器会调用aa的析构函数,(5)当main函数结束时调用aa2的析构函数

优化后,(1)在函数f2中调用了默认构造函数创建一个A类对象aa2,(2)从结果上来看,编译器可能是只创建了aa2,将aa类似一个指针的效果指向aa2,因此只在在main函数结束时调用了析构函数析构aa2,而没有析构aa

A f2()
{A aa;cout << &aa2 << endl;return aa;
}
int main()
{cout << &aa2 << endl;A aa2 = f2();
}	

从下面的图中我们可以看到,aa和aa2的地址是相同的。 

这篇文章主要目的是为了介绍编译器的对代码的优化,以及学习程序运行的语法,可能有表述不严谨的地方,欢迎大家探讨纠正。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

kubernetes调度2

1、各种缩写的应用 [rootk8s-master test]# kubectl get rsNAME DESIRED CURRENT READY AGEtest001-64c7957b5c 2 2 2 8m59stest001-698b98bb8f 0 0 0 12m[rootk8s-master test]# kubectl get replicas…

XML映射器-动态sql

01-动态sql 1.实现动态条件SQL 第一种方法在sql语句中加入where 11其他条件都加and就行,这样就可以根据if条件来判断要传递的参数可以有几个 第二种方法用where标签给if语句包起来 where标签的作用如下图 第三种方法用trim标签解释如下图 用choose也可以实现条件查询如下图,…

【RabbitMQ】死信队列、延迟队列

死信队列 死信&#xff0c;简单理解就是因为种种原因&#xff0c;无法被消费的消息。 有死信&#xff0c;自然就有死信队列。当一个消息在一个队列中变成死信消息之后&#xff0c;就会被重新发送到另一个交换器中&#xff0c;这个交换器就是DLX&#xff08;Dead Letter Excha…

C++入门 之 类和对象(下)

目录 一、初始化列表 二、隐式类型转换与explict 三、静态成员——static 四、友元 五、内部类 六、匿名对象 七.对象拷贝时的编译器优化 一、初始化列表 之前我们实现构造函数时&#xff0c;初始化成员变量主要使用函数体内赋值&#xff0c;构造函数初始化还有一种方式&…

信奥初赛解析:1-3-计算机软件系统

知识要点 软件系统是计算机的灵魂。没有安装软件的计算机称为“裸机”&#xff0c;无法完成任何工作硬件为软件提供运行平台。软件和硬件相互关联,两者之间可以相互转化&#xff0c;互为补充 计算机软件系统按其功能可分为系统软件和应用软件两大类 一、系统软件 系统软件是指…

SpringCloud微服务实现服务降级的最佳实践

Spring Cloud是一种用于快速构建分布式系统的框架&#xff0c;它提供了许多有用的功能&#xff0c;其中包括服务降级。 服务降级是一种保护机制&#xff0c;它可以在面临高并发或故障时保持服务的稳定性。当系统资源不足或服务出现故障时&#xff0c;服务降级可以通过关闭一些功…

react 组件化开发_生命周期_表单处理

组件基本介绍 我们从上面可以清楚地看到&#xff0c;组件本质上就是类和函数&#xff0c;但是与常规的类和函数不同的是&#xff0c;组件承载了渲染视图的 UI 和更新视图的 setState 、 useState 等方法。React 在底层逻辑上会像正常实例化类和正常执行函数那样处理的组件。 因…

Unsupervised Deep Representation Learning for Real-Time Tracking

摘要 我们的无监督学习的动机是稳健的跟踪器应该在双向跟踪中有效。具体来说&#xff0c;跟踪器能够在连续帧中前向定位目标对象&#xff0c;并回溯到其在第一帧中的初始位置。基于这样的动机&#xff0c;在训练过程中&#xff0c;我们测量前向和后向轨迹之间的一致性&#xf…

95、k8s之rancher可视化

一、ranker 图形化界面 图形化界面进行k8s集群的管理 rancher自带监控----普罗米修斯 [rootmaster01 opt]# docker load -i rancher.tar ##所有节点 [rootmaster01 opt]# docker pull rancher/rancher:v2.5.7 ##主节点[rootmaster01 opt]# vim /etc/docker/daemon.jso…

C++初阶学习——探索STL奥秘——反向迭代器

适配器模式是 STL 中的重要组成部分&#xff0c;除了容器适配器外&#xff0c;还有 选代器适配器&#xff0c;借助 选代器适配器 &#xff0c;可以轻松将各种容器中的普通迭代器转变为反向迭代器&#xff0c;这正是适配器的核心思想 注:库中的反向迭代器在设计时&#xff0c;为…

HashMap线程不安全|Hashtable|ConcurrentHashMap

文章目录 常见集合线程安全性HashMap为什么线程不安全&#xff1f;怎么保证HashMap线程安全 HashtableConcurrentHashMap 引入细粒度锁代码中分析总结 小结 常见集合线程安全性 ArrayList、LinkedList、TreeSet、HashSet、HashMap、TreeMap等都是线程不安全的。 HashTable是线…

【Python报错已解决】To update, run: python.exe -m pip install --upgrade pip

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

【C++篇】~类和对象(中)

类和对象&#xff08;中&#xff09; 1.类的默认成员函数​ 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。一个类&#xff0c;我们不写的情况下编译器会默认生成以下6个默认成员函数&#xff0c;需要注意的是这6个中最重要的是前…

【LeetCode每日一题】——401.二进制手表

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 回溯 二【题目难度】 简单 三【题目编号】 401.二进制手表 四【题目描述】 二进制手表顶部…

arcgisPro地理配准

1、添加图像 2、在【影像】选项卡中&#xff0c;点击【地理配准】 3、 点击添加控制点 4、选择影像左上角格点&#xff0c;然后右击填入目标点的投影坐标 5、依次输入四个格角点的坐标 6、点击【变换】按钮&#xff0c;选择【一阶多项式&#xff08;仿射&#xff09;】变换 7…

1.Seata 1.5.2 seata-server搭建

一&#xff1a;Seata基本介绍 Seata是一款开源的分布式事务解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 详见官网链接&#xff1a;https://seata.apache.org/zh-cn/ 1.历史项目里的使用经验&#xff1a; 之前公司里的oem用户对应的App…

多重指针变量(n重指针变量)实例分析

0 前言 指针之于C语言&#xff0c;就像子弹于枪械。没了子弹的枪械虽然可以用来肉搏&#xff0c;却失去了迅速解决、优雅解决战斗的能力。但上了膛的枪械也非常危险&#xff0c;时刻要注意是否上了保险&#xff0c;使用C语言的指针也是如此&#xff0c;要万分小心&#xff0c;…

【VUE3.0】动手做一套像素风的前端UI组件库---先导篇

系列文章目录 【VUE3.0】动手做一套像素风的前端UI组件库—Button 目录 系列文章目录引言准备素材字体鼠标手势图 创建vue3项目构建项目1. 根据命令行提示选择如下&#xff1a;2. 进入项目根目录下载依赖并启动。3. 设置项目src路径别名&#xff0c;方便后期应用路径。4. 将素…

solana项目counter,测试过程中执行报错记录分享

跟随HackQuest部署counter项目&#xff0c;使用 Solana 官方提供的 playgroud 。这个平台让我们的部署和测试过程变得更加简便高效。 合约代码 lib.rs中复制以下代码 use anchor_lang::prelude::*; use std::ops::DerefMut;declare_id!("CVQCRMyzWNr8MbNhzjbfPu9YVvr97…

Amoco:一款针对二进制源码的安全分析工具

关于Amoco Amoco是一款功能强大的二进制源码静态分析工具&#xff0c;该工具基于Python 3.8开发&#xff0c;可以帮助广大研究人员轻松对二进制程序执行静态符号分析。 工具特性 1、一个通用的指令解码框架&#xff0c;旨在减少实现对新架构的支持所需的时间。例如&#xff0c…