C++中的返回值优化(RVO)

一、命名返回值优化(NRVO)

是Visual C++2005及之后版本支持的优化。

具体来说,就是一个函数的返回值如果是一个对象。那么,正常的返回语句的执行过程是,把这个对象从当前函数的局部作用域,或者叫当前函数的栈空间,拷贝到返回区,使得调用者可以访问。然后程序从当前函数中返回到上一层,即该函数的调用语句处,通过访问返回区的对象,来执行调用语句所在的一整个语句。

当这个函数中所有的返回语句全部是这一个对象的话,那么,命名返回值优化的作用就是,在这个对象建立的时候,直接在返回区建立。这样就使得函数返回时不需要调用拷贝构造函数了,减少了一个对象的创建与销毁过程。

代码如下:

#include <iostream>
using namespace std;class A
{
public:A(){member = 1;cout << "default constructor" << endl;}A(const A &right){member = right.member;cout << "copy constructor" << endl;}~A(){cout << "destructor" << endl;}A& operator = (const A &right){cout << "assigning operator" << endl;return *this;}int member;
};A MyMethod(int n)
{A retVal;retVal.member = n;return retVal;
}int main()
{A valA;valA = MyMethod(10);return 0;
}

在VS2010的命令行下,进行未优化的编译: cl /Od example.cpp(cl编译的优化选项附文末)。执行example.exe,得到输出结果如下图

再次编译,进行优化后的编译: cl /O2 example.cpp ,执行example.exe,得到输出结果如下图

注:NRVO在多层嵌套函数下依然有效。

实验代码:

#include <iostream>
using namespace std;class A
{
public:A(){member = 1;cout << "default constructor" << endl;}A(const A &right){member = right.member;cout << "copy constructor" << endl;}~A(){cout << "destructor" << endl;}A& operator = (const A &right){cout << "assigning operator" << endl;return *this;}int member;
};A f()
{A tmp;return tmp;
}
A MyMethod(int n)
{A tmp = f();return tmp;
}int main()
{A valA;valA = MyMethod(10);return 0;
}

cl /Od结果:

cl /O2结果:

 二、未命名返回值优化

还有一种未命名的返回值优化,是这样做的,在返回语句中直接创建一个临时对象并返回。

实际上,这并不太能算是一种优化,因为在/Od的cl编译下也会进行优化。所以,这里相当于一个高效的编程技巧。

如下代码:

#include <iostream>
using namespace std;class MyClass
{
public:MyClass(){cout << "default constructor at " << this << endl;}MyClass(int a, int b){cout << "normal constructor at " << this << endl;}MyClass(const MyClass &right){cout << "copy constructor at " << this << endl;}~MyClass(){cout << "destructor at " << this << endl;}
};MyClass MyMethod1(int a, int b)
{MyClass tmp1(a, b);MyClass tmp2(b, a);if(a > b){return tmp1;}else{return tmp2;}
}
MyClass MyMethod2(int a, int b)
{if(a > b){return MyClass(a, b);}else{return MyClass(b, a);}
}int main()
{MyClass m;cout << "m at " << &m << endl;m = MyMethod1(1, 2);m = MyMethod2(1, 2);return 0;
}

在MyMethod1中,经历了创建tmp1与tmp2,并根据条件返回某个tmp的过程,不具备前文所述NRVO的条件(所有返回语句都要返回一个相同对象),那么会有两个tmp对象被创建,其中一个会被拷贝到返回区,再返回到函数调用语句。

但如果在返回语句中直接构造MyClass临时对象,如MyMethod2中所示,这样就可以直接将临时对象构造在返回区中,节省了两个对象的创建与销毁的过程。

用不带优化的/Od编译后,执行后结果如下:


注:未命名的返回值优化在多层函数嵌套下依然有效。

实验代码与NRVO如出一辙:

#include <iostream>
using namespace std;class A
{
public:A(){cout << "default constructor" << endl;}A(const A &right){cout << "copy constructor" << endl;}~A(){cout << "destructor" << endl;}A& operator = (const A &right){cout << "assigning operator" << endl;return *this;}
};A f()
{return A();
}
A MyMethod(int n)
{return f();
}int main()
{A valA;valA = MyMethod(10);return 0;
}

cl /Od结果如下:

三、 隐式构造函数优化

当用赋值语句对一个对象进行赋值时,最一般的情况是先执行赋值号右侧的表达式,再将表达式的结果(一般是编译时产生的临时变量)赋值给对象。

然而,当用赋值语句对一个对象进行初始化时,则该表达式的结果就是该对象。即,不需要产生临时变量,而是直接将表达式的结果建立在该对象的位置上。

不是很好表述,代码如下:

#include <iostream>
using namespace std;class A
{
public:A(){cout << "default constructor" << endl;}A(const A &){cout << "copy constructor" << endl;}~A(){cout << "destructor" << endl;}A& operator = (const A &){cout << "operator =" << endl;return *this;}
};A func()
{return A();
}int main()
{A a = func();// A a(func())效果相同return 0;
}

运行结果如图所示:

func函数中,return右边的A()直接是在函数返回区建立的。而这个返回区,在主函数中,就变成了a。所以只需要一个构造函数。

个人的理解是这样的:在栈空间中,调用函数时,会在压入实参之前,留下一个函数返回值的类型的大小那么大的空间,作为函数的返回区。而新建立的变量a,其地址恰恰就在返回区的这个地方,这两者是完全重合的。所以,在函数返回后,无需将函数返回值作为拷贝构造函数的参数去初始化a,而是——什么都不用做。因为a所在的区域,就是函数的返回区域。

然而,当多重函数这样返回的时候,结果还是一样的。这里让我有点费解。代码如下:

#include <iostream>
using namespace std;class A
{
public:A(){cout << "default constructor" << endl;}A(const A &){cout << "copy constructor" << endl;}~A(){cout << "destructor" << endl;}
private:A& operator = (const A &){cout << "operator =" << endl;return *this;}
};A func1()
{return A();
}A func()
{return func1();
}int main()
{A a(func());return 0;
}

运行的结果和上面的是一样的,还是只执行了一次默认构造函数。
问题是,如果函数返回值的返回区确实是在实参之前,与调用者的下一个局部变量的地址是重合的话,那么,a和func()返回区的地址是重合的,可以理解。可func1()的返回区不应该是在func的栈空间中吗?怎么又会和main中的a重合呢?

现在只能理解这么多了。可能只有了解了C++的函数调用约定之后才能明白吧!

附:Visual Studio(2010)的cl优化选项:

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

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

相关文章

视频AI智剪方法:快速批量处理视频,批量剪辑视频的操作

随着科技的飞速发展&#xff0c;视频内容已是获取信息和娱乐的主要方式之一。对于视频创作者和内容生产者来说&#xff0c;如何快速、高效地处理和剪辑大量视频已成为一项重要的需求。现在借助AI技术的不断发展&#xff0c;可以更加智能、高效的处理视频。下面来看云炫AI智剪如…

VS2022 | 显示Unreal Engine日志

VS2022 | 显示Unreal Engine日志 视图 -> 其他窗口 -> Unreal Engine日志 视图 -> 其他窗口 -> Unreal Engine日志

【debug】为什么ansible中使用command出错

碎碎念 在使用ansible执行command的时候&#xff0c;遇到执行会出错的command 比如执行source打算读取环境变量的时候 错误提示为&#xff1a; 没有那个文件或目录:source 一开始以为是错误提示有问题&#xff0c;一直在testrc的路径上检查&#xff0c;但是同样一行命令使用…

一次因线程池使用不当造成生产事故OOM

美好的一天从bug结束 某日当我点开熟悉的界面&#xff0c;一个又一个请求失败的提示赫然出现在屏幕上&#xff0c;不会是昨晚上线的代码有问题吧&#xff1f; 吓得我急忙按F12查看了响应——"exception":"java.lang.OutOfMemoryError","message"…

院士专家齐聚 京彩未来联合重点研究院创建数字空间联合实验室

1月6日&#xff0c;京彩未来与北京大学数字中国研究院华南分院暨广东省数字广东研究院共同创建的“数字空间共同体联合室验室”正式挂牌运营。 著名经济学家管清友博士、北京大学数字中国研究院华南分院暨广东省数字广东研究院常务副院长李鹰教授&#xff0c;广东省数字广东研…

大数据 Hive - 实现SQL执行

文章目录 MapReduce实现SQL的原理Hive的架构Hive如何实现join操作小结 MapReduce的出现大大简化了大数据编程的难度&#xff0c;使得大数据计算不再是高不可攀的技术圣殿&#xff0c;普通工程师也能使用MapReduce开发大数据程序。 但是对于经常需要进行大数据计算的人&#xff…

蒙特卡洛算法

通过随机数获得结果的算法。 当一个问题无法通过数学推导&#xff0c;计算机无法在有限时间求解时候。 就需要考虑蒙特卡洛方法了。 当无法求得精确解时候&#xff0c;进行随机抽样&#xff0c;根据统计试验求近似解。 可行域过大&#xff0c;没有通用方法求出精确解。 主…

【设计模式】访问者模式

一起学习设计模式 目录 前言 一、概述 二、结构 三、案例实现 四、优缺点 五、使用场景 六、扩展 总结 前言 【设计模式】访问者模式——行为型模式。 一、概述 定义&#xff1a; 封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构…

linux安装codeserver实现云端开发

先看图 下载安装包 https://github.com/coder/code-server/releases 找到code-server-版本号-linux-amd64.tar.gz&#xff0c;我这里是code-server-4.16.1-linux-amd64.tar.gz 1、使用acrm用户登录目标服务器 2、切换root用户&#xff0c;创建 vscode 用户&#xff0c;并设…

STM32MP157D-DK1 STM32CubeID使用与M核开发

STM32MP157具有A7内核核M4内核&#xff0c;前面介绍的一些文章&#xff0c;都是在A7内核上进行的&#xff0c;本篇来介绍M4内核的开发&#xff0c;以及开发时要用到的STM32 CubeIDE软件的使用。 1 STM32 CubeIDE创建LED工程 STM32CubeIDE是一体式多操作系统开发工具&#xff…

云消息队列 Kafka 版生态谈第一期:无代码转储能力介绍

作者&#xff1a;娜米 云消息队列 Kafka 版为什么需要做无代码转储 云消息队列 Kafka 版本身是一个分布式流处理平台&#xff0c;具有高吞吐量、低延迟和可扩展性等特性。它被广泛应用于实时数据处理和流式数据传输的场景。然而&#xff0c;为了将云消息队列 Kafka 版与其他数…

linux日志管理

一.inode与block 访问文件的流程&#xff1a; 根据文件夹的文件名和inode号&#xff0c;找到对应的inode表&#xff0c;再根据inode表的指针找到磁盘上的真实数据 tips&#xff1a;我磁盘空间还剩很多&#xff0c;但是无法建立文件&#xff1f; 因为inode号被分完了 解决方法&a…

深度理解Flutter:有状态Widget与无状态Widget的详细对比

有状态Widget 什么是有状态Widget (StatefulWidget) 官方解释&#xff1a; 如果用户与 widget 交互&#xff0c;widget 会发生变化&#xff0c;那么它就是 有状态的。 有状态的 widget 自身是可动态改变的&#xff08;基于State&#xff09;。 例如用户交互而改变 Widget 的 s…

校招社招,认知能力测验,③如何破解语言常识类测试题?

作为认知能力测评中的一个环节&#xff0c;语言常识类&#xff0c;是大概率的出现&#xff0c;不同的用人单位可能略有不同&#xff0c;语言是一切的基础&#xff0c;而常识则意味着我们的知识面的宽度。 语言常识类的测试&#xff0c;如果要说技巧&#xff1f;难说....更多的…

洗地机什么牌子好?目前口碑最好的洗地机

如今&#xff0c;人们的生活中&#xff0c;洗地机已经成为了越来越受欢迎的清洁工具&#xff0c;洗地机能迅速而有效地清理地板、地毯以及其他硬表面&#xff0c;为用户提供更加方便快捷的洗地机体验。那么&#xff0c;洗地机什么牌子好?我们一起来看看目前口碑最好的洗地机有…

在Kubernetes中优雅地导出和清理Ingress资源

引言 Kubernetes的Ingress资源是定义外部访问集群服务的规则。随着微服务架构和容器化技术的普及&#xff0c;Ingress作为路由流量的关键组件变得愈发重要。当我们需要在环境之间迁移Ingress资源或者备份当前的配置时&#xff0c;就会用到导出功能。然而&#xff0c;直接使用k…

508基于51单片机的火灾检测与报警系统设计

基于51单片机的火灾检测与报警系统设计[proteus仿真] 火灾检测与报警系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的火灾检测与报警系统设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 …

在云服务器ECS上用Python写一个搜索引擎

在云服务器ECS上用Python写一个搜索引擎 一、场景介绍二、搜索引擎的组成2.1 网页的爬取及排序2.2 用户使用搜索引擎进行搜索 三、操作步骤3.1 环境准备3.2 安装Anaconda3.3 安装Streamlit3.4 下载搜索引擎代码3.5 运行搜索引擎 四、常见问题4.1 运行setup.py时可能的问题4.2 如…

数字孪生与物联网(IoT)技术的结合

数字孪生与物联网&#xff08;IoT&#xff09;技术的结合可以在多个领域实现更智能、更高效的应用。以下是数字孪生在物联网技术中的一些应用&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合作。 1.实时监…

Opencv实验合集——实验八:相机校准

1.定义 首先&#xff0c;我们来理解一下怎么从相机的角度去看一张图片&#xff0c;就好比如你用眼睛作为相机来进行摄影&#xff0c;但是比摄影机强的是&#xff0c;你是怎么摄影图片之后再将它矫正出现在你眼前&#xff0c;将歪歪扭扭的图片变成一张在你眼前是一张直的图片 为…