C++拷贝构造、移动构造与返回值优化

C++:类的拷贝和移动、初始化和赋值 C++:初始化和赋值、拷贝和移动 《C++Primer》学到拷贝控制这一章开始有点犯晕,拷贝和移动的各种使用条件和限制很不好理解。正如《C++Primer》中所写的,虽然初始化(尤其是拷贝初始化)和赋值看上去差不多,都使用=,但是二者区别实际上非常大。今天写了这样一段代码,结果很有意思: #include "Message.h" #include <iostream> using n... 阅读详情

转自:http://findingsea.github.io/2017/06/21/C-copy-constructor-move-constructor-and-return-value-optimization/?utm_source=tuicool&utm_medium=referral

拷贝构造函数

拷贝构造函数(又称复制构造函数),是用来创建已存在对象的副本。对应的还有一个概念是拷贝赋值运算符,当需要显示地声明拷贝构造函数时,一般建议同时声明拷贝赋值运算符,以使得代码的含义明确。

如果不声明拷贝构造函数(或拷贝赋值运算符),编译器将会生一个默认的拷贝构造函数(或拷贝赋值运算符)(当被调用时生成)。参阅 声明构造函数的规则 。

看一段典型的代码:

class TextFile {
public:
   TextFile() {};
   TextFile(const TextFile& tf) {
      cout << "copy" << endl;
   };
   TextFile& operator=(TextFile& tf) {
      cout << "operator=" << endl;
      return *this;
   };
};

int main() {

   vector<TextFile> text_files;
   text_files.push_back(TextFile());

   TextFile text_file_a;
   TextFile text_file_b;
   text_file_b = text_file_a;
}

输出如下:

copy
operator=

TextFile 类显示地定义了拷贝构造函数与拷贝赋值运算符,分别在 text_files.push_back(TextFile()); 和 text_file_b = text_file_a; 两处调用。

需要注意的是,如果写成 TextFile text_file_b = text_file_a; ,那么被调用的将会是拷贝构造函数(也就是输出 copy )。

移动构造函数

上文的程序中, text_files.push_back(TextFile()); 该行构造了两个一模一样的 TextFile 对象,一次是在 TextFile 临时对象构造时,一次是向量在 push_back 时将临时对象又拷贝一份,而第二次构造明显是可以避免的。

因此,C++11中引入了 移动构造 的概念,与临时对象发生拷贝时不需要重新分配内存而是使用被拷贝对象的资源,即临时对象的资源。移动构造函数配合右值引用完成从临时对象数据转移到新的对象中,避免了数据拷贝。

class TextFile {
public:
	TextFile() {};
    
    // 移动构造函数
	TextFile(TextFile && tf) {
		cout << "move" << endl;
	};
};

int main() {

	vector<TextFile> text_files;
	text_files.push_back(TextFile());
}

输出:

move

可以看到,当再需要 push_back 时,调用的就是移动构造而不是拷贝构造函数了,其他情况同理,以此可以节省内存分配上的性能开支。

返回值优化

这一点是我在编写样例代码时遇到的问题,讲到拷贝构造函数时,常举的一个例子如下:

class TextFile {
public:
	TextFile() {};
	TextFile(const TextFile& tf) {
		cout << "copy" << endl;
	};
	TextFile& operator=(TextFile& tf) {
		cout << "operator=" << endl;
		return *this;
	};
};

TextFile get_tmp_text_file() {
	TextFile tf;
	return tf;
}

int main() {

	TextFile tf = get_tmp_text_file();
}

这是很经典的例子,很多文章都会讲 TextFile tf = get_tmp_text_file(); 会调用两次拷贝构造函数,一次是 get_tmp_text_file 函数返回时,将函数返回拷贝到临时变量里,第二次是在 main 函数内构造 TextFile 对象时,将临时变量拷贝到 tf 对象上。所以,程序会输出两遍 copy。

然而,如果你现在写了这么一个程序,真的跑一遍,它什么都不会输出。

不要怀疑自己,代码没错,教程也没错,这是因为GCC的 返回值优化 。

当一个函数返回一个对象实例,一个临时对象将被创建并通过复制构造函数把目标对象复制给这个临时对象。C++标准允许省略这些复制构造函数,即使这导致程序的不同行为,即使编译器把两个对象视作同一个具有副作用。

这是C++标准允许编译器独立实现的优化。被称为返回值优化(RVO/NRVO)。

关于RVO/NRVO历史可以参见维基百科 Return value optimization 词条

G++编译时,会默认对代码进行返回值优化,优化后的等价代码如下:

TextFile get_tmp_text_file(TextFile * tf) {
	// 直接在tf上构造
}

int main() {
    TextFile tf;
	get_tmp_text_file(&tf);
}

因此就不需要再调用 TextFile 对象的拷贝构造函数。

要关闭这种优化,只要在编译时加上 -fno-elide-constructors 强制G++总是使用拷贝构造函数。

g++ 2DimensionArray.cpp -o run -fno-elide-constructors && ./run

输出:

copy
copy
移动构造函数的一些示例 一个移动构造函数的示例,用来帮助理解文章。 立即下载

相关推荐

LK32T102 单片机 操作GPIO实现流水灯(1)

前言: LK32T10232(32位)单片机是一款专为电机控制器和数字电源开发的高性能 MCU。采Cortex-M0 内核,最高工作频率可达 72Mhz,ADC 采样速率 达 1Msps。同时具备协处理器单元可满足各种乘法、除法、移位以及各种 复杂函数运算。 LK32T102(32位)支持单电源供电,且内嵌高精度高速及低速振荡器,以及具备 多种低功耗工作模式。同时,集成了多路增强型 PWM,多通道模拟比较 器和高速运算放大器,可满足多种电机及功率控制应用,并简化系统成本。...

qq_58676187的博客 3189

c++编译器对返回值的优化

c++编译器对返回值的优化 当返回一个局部对象时,编译器对返回值的优化 编译器不同,优化程度不同,我的只能测试出两种情况,后续会对三种情况全部进行分析 文章目录c++编译器对返回值的优化理论上返回过程分析一、理论返回过程和实际返回过程二、不同的返回值优化1.本例返回值分析2.返回去除中间商赚差价总结 理论上返回过程分析 编译器会对返回值过程进行优化,所以此阶段执行的代码效果可能存在不相同的情况 提示:以下是本篇文章正文内容,下面案例可供参考 一、理论返回过程和实际返回过程 首先,为了更加清楚明显的

lilelijian的博客 718

避开命令行!在VMware vCenter 8.0图形化界面里搞定SSL证书续期全流程

本文详细介绍了在VMware vCenter 8.0图形化界面中完成SSL证书续期的全流程,包括计算机SSL证书、VMCA根证书和STS证书的管理。通过可视化操作,管理员无需使用命令行即可高效处理证书问题,确保vSphere环境的稳定运行。文章还提供了证书监控与预警配置的实用技巧,帮助用户提前规避证书过期风险。

weixin_27051161的博客 282

C++ vector 性能优化:

若提前知道空间大小,提前分配会提高性能: vector&lt;int&gt; v; v.reserve(10000); // 注,reserve预留空间,但不创建对象 // 即在push_back或insert前不可以直接引用。 // 区别于resize(),resize()会创建对象。 for(int i = 0; i &lt; 10000; i++){ v.push_back(i); }...

ShellDawn的博客 4550

计算机网络实验——入侵检测系统实验

在图1所示的网络结构中,此方案的具体实施:在路由器Router1的接口1出方向配置入侵检测策略,在收到ICMP ECHO请求包时,丢弃并同步向日志服务器报告警告,此时,终端PC2和PC3无法对终端PC0和PC1执行ping操作,并且日志服务器将记录相应的警告信息;在实验一的基础上,添加扩展分组过滤器,指定需要进行检测的信息流的类别,将扩展分组和对应的入侵检测规则进行绑定,使得只对特定的信息流进行入侵检测。加载指定类别的特征库,这里的指定类别是 ios_ips 类别中的 basic 子类别。

放放的博客 1563

《从零搭建Vue3项目实战》(AI辅助搭建Vue3+ElemntPlus后台管理项目)零基础入门系列第一篇:开发环境准备

vue3入门实战:环境准备篇

感谢阅读 1385

【C++】 函数返回vector

当程序中要返回vector时,一般做法会将函数直接定义为: vector<T> func(){ //TODO } 而为了提高执行效率,可以直接返回该vector的引用,这样会减少vector的多次拷贝。具体写法如下: vector<T>& func(){ //TODO } 但要注意的是,该要返回的vector不能是函数内部的局部变量。 ...

xll_bit的博客 2万+

79、昇腾系列服务器/昇腾系列推理卡/昇腾系列嵌入式开发板+Dify+one api部署DeepSeek-R1-Distill-Qwen-32BW8A8+RAG本地模型知识库和负载均衡双实例

阶段,安装过程缺什么,,在docker外面git下源码,进入容器内部进行量化,这里的容器建议在创建个8卡的容器,双卡容器量化会显示npu显存不够,除非你用cpu转模型,我就懒得创建容器了,使用cpu量化吧,巨慢;创建容器,进入容器,计划使用两张昇腾NPU卡推理DeepSeek-R1-Distill-Qwen-32B的W8A8模型,所以构建的容器用两张卡,选6、7卡吧,0-6号卡可以跑文本嵌入模型、重排序模型;直接进入量化阶段,在容器外面操作即可,环境不用管,因为系统已经默认配置了环境,直接跳到。

sxj731533730 6118

C++中vector返回值的最高效返回

对于现代C++,返回局部对象并不会引发性能问题,编译器通常会采取RVO/NRVO(返回值优化/命名返回值优化)来避免多余的拷贝,对于C++11及之后的版本,利用移动语义同样可以获得高效的返回。

wangkai6666的博客 1591

(持续更新中!)详解【计算机类&面试真题】军队文职考试 ——第二期(真题+解析)| 网络协议的三个核心要素;交互式系统中的非剥夺策略;中断和陷入的区别;可变分区管理中的硬件机制;数据库系统的优点

本篇博客将讨论军队文职面试(计算机类)的几个关键问题。我们将介绍网络协议的核心要素(帮助你理解网络通信的基本原理)、交互式系统中的非剥夺策略(解释为何这是一个优秀的策略以及其对用户体验、系统稳定性和可预测性的影响)、中断和陷入的区别(从引起原因、处理机制、权限级别和应用等方面进行阐述)、可变分区管理的硬件机制以及数据库系统与文件系统的优点比较。通过阅读本文,您将深入了解这些话题,为面试准备和实际应用提供有价值的知识。

追光者♂:记录、分享、总结、提升,现象级专栏《Python从入门到人工智能》作者,无惧黑暗,坚信曙光 1706

【零基础入门unity游戏开发——3D篇】3D物理关节 —— Joint相关组件

【零基础入门unity游戏开发——3D篇】3D物理关节 —— Joint相关组件

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1200

c++类指针赋值表达式必须是可修改的左值_C++的移动构造和移动赋值运算符

原文链接:C++11 Tutorial: Introducing the Move Constructor and the Move Assignment Operator​smartbear.com由于本人才疏学浅,翻译难免有误,望各位不吝惜指正。右值引用右值引用(rvalue references)是一种新的用于绑定右值的引用类型。那么什么是右值?右值通常是编译器生成的用于表达式计算的临时变量...

weixin_39620279的博客 307

实干派!网易易盾增强版滑块验证码,全方位讲解识别思路(含源码)

该验证码是异形滑块验证码中的一种,缺口旋转+偏移的处理需要你有很好的图像处理知识,能很好的处理透明通道,同时,该验证码需要你能很好的处理缺口和背景之间匹配的关系,该验证码的处理难点就是两个缺口之间如何找到正确的,两个检测框都有很强的相似度,且加上背景图的干扰,在图像处理和有优化上难度大大增加。

码王吴彦祖的博客 2015

拷贝(复制)构造、移动构造与返回值优化

C++拷贝构造、移动构造与返回值优化拷贝构造函数移动语义(移动构造、移动赋值)右值引用补充: move()函数编译器优化拷贝构造函数拷贝构造函数(又称复制构造函数),用来创建已存在对象的副本。用已有对象去初始化另一个对象。还有拷贝赋值运算符,当需要显示地声明拷贝构造函数时,一般建议同时声明拷贝赋值运算符。如果不声明拷贝构造函数(或拷贝赋值运算符),编译器将会生一个默认的拷贝构造函数(或拷贝赋值运算符)(当被调用时生成)。注意:区分赋值和拷贝构造,也就是区分赋值与初始化。

az44yao的专栏 120

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

C++ 函数返回值优化

如果编译器支持返回值优化(RVO,Return Value Optimization)或者具名返回值优化(NRVO,Named Return Value Optimization),那么在返回。对于上面的例子,通过返回值优化(RVO)或具名返回值优化(NRVO),编译器可以省略掉。的资源(如动态分配的内存)可以被移动而不是复制,那么编译器会调用移动构造函数来将。如果没有应用RVO/NRVO,但编译器支持移动语义(C++11及以后版本),),在这种情况下,编译器可能会优化掉不必要的拷贝。

weixin_51883798的博客 920

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

现代C++之如何返回一个对象?

如何返回一个对象?一个用来返回的对象,通常应当是可移动构造 / 赋值的,一般也同时是可拷贝构造 / 赋值的。如果这样一个对象同时又可以默认构造,我们就称其为一个半正则(semiregul...

Francis的博客 5113

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

以历史角度看优化 -- C++历程对于函数返回值的优化

C++返回值优化核心总结: NRVO(具名返回值优化):初始化+返回具名对象=0拷贝0移动,直接构造 RVO(匿名返回值优化):初始化+返回匿名对象=0拷贝0移动,最优方案 NRVO失效情况:先定义后赋值=1次移动构造+1次移动赋值 强制关闭优化:保留移动语义时=2次轻量移动 C++98优化:1次深拷贝 C++98不优化:2次深拷贝(最慢) 关键记忆点:初始化写法配合单一返回路径可获得最优性能,移动语义比拷贝快,C++11优化机制显著提升效率。

BinaryWiker的博客 366

C++基础_关于拷贝构造函数(复制构造函数)RVO

VS2022会直接跳过拷贝构造或移动构造,直接在目标对象内存中构造返回对象,优化性能

学海无涯 1256

c++移动构造函数、拷贝构造、赋值构造的区别

拷贝构造函数和移动构造函数的区别。

qq_42176731的博客 578

C++进阶之拷贝构造函数:用法实例(三百零八)

本篇目的:C++进阶之拷贝构造函数:用法实例基本概念拷贝构造函数是C++中的特殊构造函数,用于创建一个新对象,并使用同类型的已有对象对其进行初始化。它的作用是确保新对象复制已有对象的所有成员,并保持数据的一致性。拷贝构造函数的参数通常是对已有对象的常引用,以避免不必要的递归调用和内存开销。拷贝构造函数的默认行为如果类没有显式定义拷贝构造函数,编译器会自动生成一个默认的拷贝构造函数。默认拷贝构造函数执行的是浅拷贝,即按成员变量的位模式进行逐个拷贝。

Android系统攻城狮 1641

C++ 拷贝构造函数 和 六大函数

1. C++什么时候会调用 拷贝构造函数? a.一个对象作为函数参数,以值传递的方式传入函数体; b.一个对象作为函数返回值,以值传递的方式从函数返回;(实际使用时,会被编译器优化掉) c.一个对象用于给另外一个对象进行初始化(常称为赋值初始化) 如: Animal a; Animal b(a); // 或者 Animal a...

weixin_33777877的博客 1191

返回类对象时,什么时候调用拷贝构造函数,什么时候会进行返回值优化(RVO)

在C++中,返回值优化(Return Value Optimization,RVO)是一种编译器优化技术,用于避免不必要的对象拷贝。需要注意的是,对于编译器优化的行为并没有明确的规定,不同的编译器和编译器设置可能会有不同的结果。在某些情况下,编译器可能会对这些情况进行优化,避免不必要的拷贝构造函数的调用,这取决于编译器的实现和优化策略。需要注意的是,返回值优化是编译器的一种优化技术,不是C++标准的要求。不同的编译器可能有不同的优化策略,因此在其他编译器或编译器设置下,可能会触发拷贝构造函数的调用。

周周的博客 872

C++移动构造函数

对于了解移动构造函数的工作原理,我们需要在编译时设置编译选项-fno-elide-constructors以关闭返回值优化效果,从样就可以看到完整的移动构造函数调用过程,避免编译器优化,有些地方看不到调用移动构造函数的过程。-fno-elide-constructors这个参数只是针对C++的,手册对该参数的描述如下: -fno-elide-constructors The...

J_H_C的博客 3234
上一篇: 保护共享数据
下一篇: 同步并发操作
早起的虫儿灬
博客等级 码龄10年 54粉丝 114原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值