C++中拷贝对象时编译器做出的一些优化

目录

拷贝对象时编译器做出的一些优化

参数传递优化

返回值优化


拷贝对象时编译器做出的一些优化

📌

下面的优化结果由编译器决定,不同的编译器优化结果可能不同,视具体情况而定

参数传递优化

  1. 在前面的explicit关键字部分提到过编译器会对在单行的构造+拷贝构造优化为构造
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){}void print(){cout << _num << endl;}
};int main()
{test t = 1;t.print();return 0;
}
输出结果:
1
  1. 在给函数形参传递实参时,如果直接传递已经创建的对象时,编译器不会对其进行优化
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test t)
{cout << "func1" << endl;
}int main()
{test t1(1);func1(t1);return 0;
}
输出结果:
构造函数
拷贝构造函数
func1
析构函数
析构函数

在上面的代码中,首先test类创建了一个对象为t1,此时调用构造函数,当t1作为函数实参传递给func1函数,此时会调用拷贝构造函数将t1对象拷贝给形参t,接着进入func1函数栈帧空间执行func1函数体的语句,当func1函数结束执行后调用析构函数销毁形式参数对象t,最后调用析构函数销毁局部对象t1

对上面的代码进行改进,直接传递整型1给func1函数,如下面代码

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test t)
{cout << "func1" << endl;
}int main()
{func1(1);return 0;
}
输出结果:
构造函数
func1
析构函数

在上面的代码中,直接将1作为对象传递给自定义类型的形参t时,常规的步骤为:调用构造函数用整型1初始化一个临时对象,再调用拷贝构造函数将临时对象中的内容拷贝给形参对象,但是此处编译器会对其进行优化为直接调用构造函数,用整型1初始化形参对象t

同理,使用匿名对象作为实际参数传递给自定义类型的形参时,编译器也会有所优化

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test t)
{cout << "func1" << endl;
}int main()
{func1(test(2));return 0;
}
输出结果:
构造函数
func1
析构函数

在上面的代码中,使用整型2创建了一个匿名对象,常规步骤为:调用构造函数使用整型2创建匿名对象,接着调用拷贝构造函数将匿名对象中的内容拷贝给形式参数,但是编译器优化为直接使用整型2为形式参数初始化

但是如果函数的形式参数为引用时,则不会有任何优化,直接调用构造函数进行初始化对象再由自定义类型的引用形参接收实参对象的地址

📌

注意:使用引用传参时一定要在形式参数处加const修饰

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test& t)
{cout << "func1" << endl;
}int main()
{test t1(1);func1(t1);cout << endl;func1(1);cout << endl;func1(test(1));return 0;
}
输出结果:
构造函数
func1构造函数
func1
析构函数构造函数
func1
析构函数
析构函数

返回值优化

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;test t(1);return t;
}int main()
{func();return 0;
}
  1. 当调用的函数有返回对象时,使用该对象初始化对象
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;test t(1);return t;
}int main()
{test t1 = func();return 0;
}

在上面的代码中,使用func函数的返回值初始化t1对象,常规的过程为:调用拷贝构造函数将func函数的返回值放入一个自定义类型的临时变量中,再通过拷贝构造函数将临时变量中的内容拷贝给t1对象,但是这里编译器会优化为调用一个构造函数将func的返回值作为初始化值直接初始化t1对象

但是如果将两个步骤分开,如下面的代码

#include <iostream>
using namespace std;class test
{
private:int _num;
public:test():_num(){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;test t;return t;
}int main()
{test t1;t1 = func();return 0;
}

在上面的代码中,因为t1对象需要完成实例化,所以会调用构造函数,接着执行t1 = func()语句,因为赋值运算符有从右往左的结合性,所以先执行func函数,在func函数中会再次调用构造函数创建一个对象,(注意中间有一个过程为:调用拷贝构造将返回对象拷贝到临时对象中,再调用析构函数销毁局部对象t)此时执行赋值语句,此时调用赋值运算符重载函数,将t对象的内容给t1对象

  1. 当返回的是匿名对象,使用该匿名对象初始化对象
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test():_num(){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;return test();
}int main()
{test t1 = func();return 0;
}
输出结果:
func
构造函数
析构函数

在上面的代码中,先执行func函数,常规步骤为:执行test类中的构造函数创建一个匿名对象,接着调用拷贝构造将匿名对象拷贝到临时对象中返回,接着调用拷贝构造将返回值拷贝给t1对象,但是此处编译器会优化为直接用返回的匿名对象的内容作为初始值初始化对象t1

总结:

  1. 为了编译器更好得优化,在传参数时,可以考虑使用引用变量作为参数
  2. 当使用到返回值时,如果能用引用就使用引用,不能使用引用需要返回值时,可以考虑返回匿名对象

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

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

相关文章

neo4j所有关系只显示RELATION,而不显示具体的关系

当看r时&#xff0c;真正的关系在properties中的type里&#xff0c;而type为“RELATION” 造成这个的原因是&#xff1a; 在创建关系时&#xff0c;需要指定关系的类型&#xff0c;这是固定的&#xff0c;不能像属性那样从CSV文件的一个字段动态赋值。标准的Cypher查询语言不支…

人工智能之Tensorflow变量作用域

在TensoFlow中有两个作用域&#xff08;Scope&#xff09;&#xff0c;一个时name_scope ,另一个是variable_scope。variable_scope主要给variable_name加前缀&#xff0c;也可以给op_name加前缀&#xff1b;name_scope给op_name加前缀。 variable_scope 通过所给的名字创建或…

C++中类和对象其他内容

目录 explicit关键字 static成员 static成员的介绍 static成员的使用 友元 友元函数 友元类 内部类 匿名对象 explicit关键字 在C中&#xff0c;给类对象初始化时会调用类的构造函数&#xff0c;但是也可以使用赋值运算符为构造函数只有一个参数&#xff08;或者只有…

Stable diffusion(四)

训练自己的Lora 【DataSet】【Lora trainer】【SD Lora trainer】 前置的知识 batch size&#xff1a;模型一次性处理几张图片。一次性多处理图片&#xff0c;模型能够综合捕捉多张图片的特征&#xff0c;最终的成品效果可能会好。但是处理多个batch size也意味着更大的显存…

nvm更换node.js的版本

自行下载nvm 打开cmd 1. nvm ls 列出目前已经下载的node版本&#xff0c;和正在使用的node版本 2. nvm install v版本号 下载某个版本 3. nvm uninstall v版本号 卸载某个版本 4. nvm use 版本号 切换到某个版本

深入理解栈和队列(二):队列

个人主页&#xff1a;17_Kevin-CSDN博客 专栏&#xff1a;《数据结构》 一、队列的概念和结构 队列是只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出 FIFO(First In First Out) 入队列&#xff1a;进行插入操作的…

吴恩达2022机器学习专项课程(一) 3.5 可视化成本函数

问题预览 为什么要可视化成本函数&#xff1f;可视化之后的成本函数是什么样子&#xff1f;如何在三维空间里通过w和b找到一个成本函数的值&#xff1f;如何在三维空间里找到成本函数的最小值&#xff1f; 解读 可视化成本函数&#xff1a;为了更加方便的看到不同的w和b&…

AI:152- 利用深度学习进行手势识别与控制

本文收录于专栏:精通AI实战千例专栏合集 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 每一个案例都附带关键代码,详细讲解供大家学习,希望可以帮到大家。正在不断更新中~ 一. 利用深度学习进行手势识别与控制 …

Acrobat Pro DC ----专业PDF编辑与管理

Acrobat Pro DC 2023是一款功能强大的PDF处理软件&#xff0c;它提供了丰富的编辑工具&#xff0c;支持创建、编辑、合并、分割PDF文件&#xff0c;以及高质量的PDF到其他格式的转换功能。同时&#xff0c;该软件集成了最新的OCR技术&#xff0c;可将扫描文档或图片转换成可编辑…

转座子插入位点分析4------PS转座子测序数据分析

观察数据 这是经公司使用fastp质控后的数据&#xff0c;我们先挑选部分数据进行比对&#xff0c;观察序列结构 为了准确性&#xff0c;我们再次挑选另一批数据进行比对 可以看到&#xff0c;所有序列都存在一个“GTGTCAAATACTTATTTTCCCCGCTGTA”的前导序列&#xff0c;这可能…

《大厂面试模拟(免费) - C++工程方向》

以个人的名义&#xff0c;提供c工程方向的大厂面试模拟&#xff0c;不会以任何形式收费。此面试可为应聘者提供真实反馈。简历和面试过程不会以任何形式给第三方&#xff08;包括我当前所在公司&#xff09;。 我的经验&#xff1a; 多年百度&#xff0c;阿里知名核心项目组工…

Linux快速安装FFmpeg、ffprobe、ffplay以及在Linux上的使用

文章目录 一、工具简介二、CentOS7上安装FFmpeg2.1 方法一&#xff1a;yum安装2.2 方法二&#xff1a;源码安装 三、ffprobe 在linux中的用法3.1 显示多媒体文件的基本信息3.2 显示特定流的信息3.3 获取音视频的时长 一、工具简介 这些工具都是与多媒体处理和流媒体相关的开源…

uniapp页面嵌套其他页面的实现

功能: 类似于一个drawer&#xff0c;当主页面加载的时候会一并加载url对应的组件&#xff0c;当点击后以drawer形式显示组件里面的内容&#xff0c;可动画。 <navigator url"/pages/my/components/personalMessage" slot"right"><view><di…

Java螺旋折线

题目描述 如图所示的螺旋折线经过平面上所有整点恰好一次。 对于整点 (X,Y)&#xff0c;我们定义它到原点的距离dis(X,Y) 是从原点到 (X,Y) 的螺旋折线段的长度。 例如 dis(0,1)3&#xff0c;dis(−2,−1)9。 给出整点坐标 (X,Y)&#xff0c;你能计算出 dis(X,Y) 吗&#xf…

GO-初识包管理

初识包管理&#xff0c;知道项目中文件和文件夹之间的关系 输出&#xff0c;代码&#xff0c;在go编译器运行时会显示在屏幕中 初识数据类型 整型&#xff0c;数字。例如&#xff1a;1、2、3、4 字符串类型&#xff0c;表示文本信息的。例如:“张三”“李四” 布尔类型&#x…

图解Kafka架构学习笔记(三)

准备Kafka环境 这里推荐使用Docker Compose快速搭建一套本地开发环境。 以下docker-compose.yml文件用来搭建一套单节点zookeeper和单节点kafka环境&#xff0c;并且在8080端口提供kafka-ui管理界面。 version: 2.1services:zoo1:image: confluentinc/cp-zookeeper:7.3.2hos…

FPGA 以太网传输ov5640视频

1 实验任务 使用 DFZU4EV MPSoC 开发板及双目 OV5640 摄像头其中一个摄像头实现图像采集&#xff0c;并通过开发板上的以太网接口发送给上位机实时显示。 2 系统框架 时钟模块用于为 I2C 驱动模块、以太网顶层模块和开始传输控制模块提供驱动时钟&#xff1b;I2C 驱动模块…

Python Flask 自定义错误页面

新建templates/404.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head> <body> <img src"../static/a123.png" alt"" width"…

基于spring boot的个人博客系统的设计与实现(带源码)

随着国内市场经济这几十年来的蓬勃发展&#xff0c;突然遇到了从国外传入国内的互联网技术&#xff0c;互联网产业从开始的群众不信任&#xff0c;到现在的离不开&#xff0c;中间经历了很多挫折。本次开发的个人博客系统&#xff0c;有管理员&#xff0c;用户&#xff0c;博主…

使用Linux别名简化命令输入

Linux定义命令别名&#xff0c;解决经常重复输入长命令 在Linux环境下工作时&#xff0c;我们经常需要输入长长的命令&#xff0c;这不仅耗时而且容易出错。Linux提供了一种名为“别名&#xff08;alias&#xff09;”的功能&#xff0c;可以让我们为这些长命令定义简短的名称…