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

相关文章

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的

Visual Studio 2022 编译C++20代码的图文步骤

《VisualStudio2022编译C++20代码的图文步骤》在VisualStudio中启用C++20import功能,需设置语言标准为ISOC++20,开启扫描源查找模块依赖及实验性标... 默认创建Visual Studio桌面控制台项目代码包含C++20的import方法。右键项目的属性:

MyBatisPlus如何优化千万级数据的CRUD

《MyBatisPlus如何优化千万级数据的CRUD》最近负责的一个项目,数据库表量级破千万,每次执行CRUD都像走钢丝,稍有不慎就引起数据库报警,本文就结合这个项目的实战经验,聊聊MyBatisPl... 目录背景一、MyBATis Plus 简介二、千万级数据的挑战三、优化 CRUD 的关键策略1. 查

c++中的set容器介绍及操作大全

《c++中的set容器介绍及操作大全》:本文主要介绍c++中的set容器介绍及操作大全,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录​​一、核心特性​​️ ​​二、基本操作​​​​1. 初始化与赋值​​​​2. 增删查操作​​​​3. 遍历方

解析C++11 static_assert及与Boost库的关联从入门到精通

《解析C++11static_assert及与Boost库的关联从入门到精通》static_assert是C++中强大的编译时验证工具,它能够在编译阶段拦截不符合预期的类型或值,增强代码的健壮性,通... 目录一、背景知识:传统断言方法的局限性1.1 assert宏1.2 #error指令1.3 第三方解决