【C++】拷贝对象时,编译器的偷偷优化

news2025/6/21 15:35:22

你知道吗?对于连续的”构造+拷贝构造“,编译器其实是会默默做出优化的。👻

如果你不知道这个知识点的话,那下面这道笔试题就要失分了😵。

本篇分享一个关于编译器优化的小知识,看完本篇,你就能知道程序里的 构造函数、拷贝构造函数 究竟被调了几次~

题目引入

某笔试题:下面的程序经历了几次构造?几次拷贝构造?

答案为:

1次构造,4次拷贝

对此题的分析在本篇末尾。如果你对这道题尚有存疑,那相信看完本篇,你的疑惑将会烟消云散~👻

传值传参 的优化

我们知道,传值传参会产生拷贝,因为这样能保证 函数内部对参数的操作 不会影响到原始变量的值。

➡️先看一个没做优化的例子:

class W
{
public:
    //构造函数
    W(int w) 
        :_w(w)
    {
        cout << "W()" << endl;
    }
    //拷贝构造函数
    W(const W& w) 
    :_w(w._w)
    {
        cout << "W(const W& w)" << endl;
    }
    //析构函数
    ~W(){
        cout << "~W()" << endl;
    }
private:
    int _w;
};
void func(const W w) {
    
}
int main()
{
    W w1=1;  //构造
    func(w1);   //拷贝构造:把w1的值拷贝一份,传到形参
    return 0;
}

调用情况我们打印出来:

可见这种情况下:

W w1=1;
func(w1);

是老老实实调用构造+拷贝构造的,并没有做出优化。

➡️再看一个做了优化的例子:

class W
{
public:
    //构造函数
    W(int w) 
        :_w(w)
    {
        cout << "W()" << endl;
    }
    //拷贝构造函数
    W(const W& w) 
    :_w(w._w)
    {
        cout << "W(const W& w)" << endl;
    }
    //析构函数
    ~W(){
        cout << "~W()" << endl;
    }
private:
    int _w;
};
void func(const W w) {
    
}
int main()
{
    func(W(1));  //一个表达式步骤中,有连续的”构造+拷贝构造“
    return 0;
}

结果:

我们发现:这次没调拷贝构造了!看来编译器做了优化。

原本,这个表达式的执行顺序是:W(1)先构造出一个W,这个W再拷贝构造,传递给形参,参与表达式func()。

func(W(1));

而编译器做出了优化:

将连续的 构造+拷贝构造 优化成一步构造。

动图可以更直观地看出来:

传匿名对象 的优化

我们学过的匿名对象,也是可以传参过去的,它的优化和刚刚讲的传值传参的优化 道理是一样的。

class W
{
public:
    W() {
        cout << "W()" << endl;
    }
    W(const W& w) {
        cout << "W(const W& w)" << endl;
    }
    ~W(){
        cout << "~W()" << endl;
    }
};
void f1(W w) {
​
}
int main()
{
    f1(W());   //先构造一个匿名对象,然后作为参数 传值传参过去
    return 0;
}

结果:

依然没调用拷贝构造。

看看程序怎么运行的:

还是刚刚讲的,这是因为编译器的优化,将 构造+拷贝构造 直接优化成了 一步构造。

结论:一个连续的表达式步骤中,连续构造一般都会被优化。

传值返回

可以优化的情况:

class W
{
public:
    //构造函数
    W(int w=0) 
        :_w(w)
    {
        cout << "W()" << endl;
    }
    //拷贝构造函数
    W(const W& w) 
    :_w(w._w)
    {
        cout << "W(const W& w)" << endl;
    }
    //析构函数
    ~W(){
        cout << "~W()" << endl;
    }
private:
    int _w;
};
​
W func() {
    W ret;  //1次构造
    return ret;  //1次拷贝构造:将ret的值通过拷贝构造返回
}
​
int main()
{
    W w1=func();  //W w1=……仍是一次拷贝构造。所以按理说是“1构造+2拷贝构造”
    return 0;
}

然而实际上只进行了“1构造+1拷贝构造”:

为什么拷贝构造从2次变成了1次?

原来,这也是编译器的优化,它会将两个拷贝构造,优化成一个。

这里用图说明一下:

不能优化的情况

上面这种情况和下面的这种要区分,下面这种是不能优化的:

class W
{
public:
    //构造函数
    W(int w=0) 
        :_w(w)
    {
        cout << "W()" << endl;
    }
    //拷贝构造函数
    W(const W& w) 
    :_w(w._w)
    {
        cout << "W(const W& w)" << endl;
    }
    //赋值运算符
    W& operator=(const W& w) {
        cout << "W& operator=(const W& w)" << endl;
        if (this != &w) {
            _w = w._w;
        }
        return *this;
    }
    //析构函数
    ~W(){
        cout << "~W()" << endl;
    }
private:
    int _w;
};
W func() {
    W ret;
    return ret;
}
​
int main()
{
    W w1; 
    w1 = func();  //赋值接收对象
    return 0;
}

这种是分4步进行的:

可见,赋值接收对象 不如 拷贝构造的方式接收。后者可以被优化。

总结

1.连续的 构造和拷贝构造 会被优化成 直接调用构造。(分步的就无法优化了)

2.产生的临时变量往往会被优化掉。

题目的解析

我们现在回过头看看一开始那道题:

首先,W x;是1次构造。

然后,f(x)会把x的值拷贝给u,是1次拷贝构造。

在函数f(W u)里,v的实例化是1次拷贝构造.

w究竟是拷贝构造还是赋值的呢?因为w原先不存在,所以不是赋值,是1次拷贝构造。

return w;(这里最易错) 原本是要拷贝产生临时变量,再用临时变量拷贝构造出y的。但经过编译器的优化,升级成一步拷贝构造。

所以,一共1构造+4拷贝构造。

把这题升级一下:

几次构造?几次拷贝构造?

和上题同理,只不过这次return w将连续的三步优化成一步。

答案:1次构造 7次拷贝构造

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

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

相关文章

阿里云服务器退款规则_退款政策全解析

阿里云退款政策全解析&#xff0c;阿里云退款分为五天无理由全额退和非全额退订两种&#xff0c;阿里云百科以云服务器为例&#xff0c;阿里云服务器包年包月支持五天无理由全额退订&#xff0c;可申请无理由全额退款&#xff0c;如果是按量付费的云服务器直接释放资源即可。阿…

C++ Primer Plus 第六章笔记

目录 if 语句 if else语句 if else if else结构 逻辑运算符--&&,||和! cctype字符函数库 条件运算符&#xff08;三目运算符) switch语句 continue和break语句 基本文件输入/输出 总结&#xff1a;本文主要介绍了分支语句和if判断语句&#xff0c;运算符和简…

vue 浏览器记住密码后,自动填充账号密码错位

亲测有效&#xff01;&#xff01;! 遇到的场景&#xff1a; 浏览器记住密码后&#xff0c;登录时自动填充账号密码&#xff0c;由于登录时只需要这两个字段所以没问题&#xff0c;见图一&#xff0c;但注册时&#xff0c;账号密码不在一处&#xff0c;见图二 原本账号应该在…

浅谈JVM内存模型与GC垃圾回收

目录 1. 摘要 2. JVM 简单介绍 3. 线程私有的有哪些&#xff1f; 4. 线程共享的有哪些&#xff1f; 5. JVM 栈中程序是如何操作数据的&#xff1f; 6. 内存泄露是什么意思&#xff1f; 7. 堆内存的分配规则 8. 垃圾回收算法 8.1 垃圾回收机制简单概括 8.2 标记清理算法…

UNIAPP之js/nvue混淆探索

因项目需要对UNIAPP的js混淆做了一些调研 混淆教程: https://uniapp.dcloud.net.cn/tutorial/app-sec-confusion.html 按照教程配置进行打包正式包进行混淆 下载正式包将 .ipa改为.zip 解压获取到HBuilder.app 右键显示包内容 获取到混淆的key 不同时间进行打包混淆同一文…

谷器数据被认证为全国信标委软工分委会全权成员单位

8月23日&#xff0c;全国信息技术标准化技术委员会软件与系统工程分技术委员会&#xff08;简称&#xff1a;全国信标委软工分委会&#xff0c;SAC/TC28/SC7&#xff09;批准通过新一批成员单位&#xff0c;北京谷器数据科技有限公司被成功授牌为全权成员单位。 全国信标委软件…

高校消防安全存在的问题与解决方案介绍 安科瑞 许敏

摘要 &#xff1a;本文分析了当前高校消防安全存在的问题&#xff0c;现有消防系统突出问题表现形式&#xff0c;提出高校智慧消防安全预警防控体系构建&#xff0c;并对智慧消防安全预警平台主要功能进行了简介&#xff0c;对高校智慧消防安全预警系统功能设计具有一定的指导意…

0基础学习VR全景平台篇 第96篇:VR电子楼书

大家好&#xff0c;欢迎观看蛙色VR官方系列课程——VR电子楼书&#xff01; 作为2021年底全新上线的行业解决方案&#xff0c;是专门针对地产、园区数字化营销的一站式VR解决方案&#xff0c;为行业潜在客户提供优质的7x24小时线上看房体验。 本期教程将通过功能介绍后台操作&…

koa2快速2分钟搭建web服务器

koa简介&#xff1a; Koa是一个基于Node的web服务web服务器开发框架。类似于Express。 Koa1它的主要特点是&#xff0c;使用了ES6的Generator函数。 koa2依赖Node.js 7.6.0或者更高版本。 koa不在内核方法中绑定任何中间件&#xff0c;它仅仅是一个轻量级的函数库&#xff0c;几…

【QT】QMessageBox消息框的使用(16)

在实际项目中&#xff0c;弹出消息框是一个很常见的操作&#xff0c;包含错误信息提示、警告信息提示、关于信息提示、还包括判断信息选择等操作&#xff0c;那么今天通过这一节来好好了解下消息框的使用方法。 一.环境配置 1.python 3.7.8 可直接进入官网下载安装&#xf…

图:关键路径

1. AOE网 与AOV网不同&#xff0c;AOE是用边表示活动的图或者网。 1.AOE网的概念 在带权有向图中&#xff0c;以顶点表示事件&#xff0c;以有向边表示活动&#xff0c; 以边上的权值表示完成该活动的开销(如完成活动所需的时间)&#xff0c; 称之为用边表示活动的网络&…

如何尽快了解企业文化并融入团队

点击下方关注我&#xff0c;然后右上角点击...“设为星标”&#xff0c;就能第一时间收到更新推送啦~~~ 每个公司都有自己独特的企业文化&#xff0c;企业文化分为两个部分&#xff0c;一个部分是对外的企业文化&#xff0c;一个部分是企业内部的文化。对外的是公司的形象&…

并行计算之计算平台体系结构(流水线,异构处理器与异构并行)

并行计算粒度 分布式并行(多主机&#xff0c;多进程) &#xff1a; MPI 共享存储式并行 (多线程) &#xff1a;OpenMP OpenCL OpenACC 指令级并行 CPU流水线 CPU流水线 指将一条指令的执行过程划分为多个阶段&#xff0c;使得在每个时钟周期内能够并行执行多条指令&#…

前端开源代码

vue大屏&#xff1a; PublicbigScreenPage–Vue3tsWindcssEchartThree.js大屏案例 PublicbigScreenPage—基于 Vue3、TypeScript、DataV、ECharts 框架的 " 数据大屏项目 介绍&#xff1a;https://blog.csdn.net/qq_40282732/article/details/105656848 Vue3.2 Echar…

AutoDock Vina 对接计算(大批量)

AutoDock Vina 1.2.0 对接计算&#xff08;大批量&#xff09; AutoDockVina 1.2.0 的示例应用&#xff1a;A) 对接多个配体 (PDB 5x72)&#xff1b;B) 使用 AutoDock4 (PDB 4ykq) 的水合对接方案与水分子对接&#xff1b;C)在锌存在的情况下使用 AutoDock4Zn 力场 (PDB 1s63)&…

ChatGLM2 源码解析:`ChatGLMModel`

# 完整的 GLM 模型&#xff0c;包括嵌入层、编码器、输出层 class ChatGLMModel(ChatGLMPreTrainedModel):def __init__(self, config: ChatGLMConfig, deviceNone, empty_initTrue):super().__init__(config)# 如果设置了empty_init&#xff0c;创建任何 PyTorch 模块时&#…

【软件测试】单元测试、集成测试、系统测试有什么区别?

单元测试、集成测试、系统测试有什么区别 1、粒度不同 集成测试bai粒度居中&#xff0c;单元测试粒度最小&#xff0c;系统du测试粒度最大。 2、测试方式不同 集成测试一般由开发zhi小组采用白盒加黑盒的方式来测试&#xff0c;单元测试一般由开发小组采用白盒方式来测试&a…

第六章:中华民族的抗日战争

1.日本发动灭亡中国的侵略斗争 关键字&#xff1a; 中国抗日战争的起点与全民族抗战阶段 2.中国人民奋起抗击日本侵略者 关键字&#xff1a; 1 国共第二次统一战线初步建立的标志&#xff1a;国民党五届三中全会 2 扭转时局的枢纽&#xff0c;国内和平初步实现&#xff1a;…

3.msfconle

目录 1 进入msfconsole 2 连接postgresql数据库 3 msfconsole基本用法 4 更新msf 5 搜索脚本 search 6 查看脚本信息 info 7 设置参数 8 重新设置参数与取消参数 9 退出当前模块 back 10 查看域名基本信息 dig 11 查看域名的详细信息 whois 1 进入msfco…

k8s部署redis 3主3从

k8s部署redis6节点&#xff0c;组成3主3从集群模式 一般来说&#xff0c;redis部署有三种模式。 单实例模式&#xff0c;一般用于测试环境。 哨兵模式 集群模式后两者用于生产部署 哨兵模式 在redis3.0以前&#xff0c;要实现集群一般是借助哨兵sentinel工具来监控master节点…