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

2024-03-25 04:44

本文主要是介绍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. 当使用到返回值时,如果能用引用就使用引用,不能使用引用需要返回值时,可以考虑返回匿名对象

这篇关于C++中拷贝对象时编译器做出的一些优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/843867

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

HDFS—存储优化(纠删码)

纠删码原理 HDFS 默认情况下,一个文件有3个副本,这样提高了数据的可靠性,但也带来了2倍的冗余开销。 Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约50%左右的存储空间。 此种方式节约了空间,但是会增加 cpu 的计算。 纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

MySQL高性能优化规范

前言:      笔者最近上班途中突然想丰富下自己的数据库优化技能。于是在查阅了多篇文章后,总结出了这篇! 数据库命令规范 所有数据库对象名称必须使用小写字母并用下划线分割 所有数据库对象名称禁止使用mysql保留关键字(如果表名中包含关键字查询时,需要将其用单引号括起来) 数据库对象的命名要能做到见名识意,并且最后不要超过32个字符 临时库表必须以tmp_为前缀并以日期为后缀,备份

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)