关于NRV优化详细分析

自己动手理解NRV优化 自己动手理解NRV优化2010.6.29一、NRV的简单理解    NRV是Nameed Return Value的简称。NRV优化简单的说:有一条语句,A a = f();其中f()是一个函数,函数里边申请了一个A的对象b,然后把对象b返回。在对象返回的时候,一般情况下要调用拷贝函数,把函数f()里边的局部对象b拷贝到函数外部的对象a。但是如果用了NRV优化,那就不必要调用拷贝构造函数,编译器可以这样做,把a的地址传递进函数f(),然后不让f()申请要返回的对象b的空间,用a的地址来代替b的地址,这样当要 阅读详情


 在C++中,函数返回整数或指针是通过eax寄存器进行传递的,理解起来比较简单。

    但是返回对象或结构体一直是令人感到困惑的问题。今天我整理了一下,将整个返回过程写下来,以作备用。

 

    还是先通过一个例子来理解这个问题:

首先,定义一个类Vector:

class Vector
{
public:
    int x,y;
};


然后定义函数add()对Vector对象进行操作:
Vector add(Vector& a, Vector & b)
{
    Vector v;
    v.x = a.x + b.x; 
    v.y = a.y + b.y;
    return v;
}

现在的问题是:

如果调用如下语句:

Vector a, b;
Vector c = add(a, b);  (调用拷贝构造函数,而不是赋值语句)

请问从a, b传入函数开始,一共创建了多少个对象?

 

在通常情况下我们会做出如下分析:

1. 在add()函数中创建对象v。

2. 函数返回,创建一个临时变量__temp0,并将v的值拷贝到__temp0中。

3. 最后创建对象c,通过操作符=,将__temp0中的对象拷贝到c中。


但其实,我们会在后面看到,整个过程就只创建了1个对象:c。

 

为了更清晰的分析整个调用过程,我们为Vector加上默认构造函数和拷贝构造函数,并增加一个静态变量count用于统计构造函数调用次数:

class Vector
{
public:
    static int count;
 
    static void init()
    {
        count = 0;
    }
 
    int x,y;
 
    Vector()
    {
        x = 0;
        y = 0;
         
        //For analysis.
        count++;
        printf("DefaultConstructor was called.[0x%08x]\n",this);
    }
 
    Vector(const Vector& ref)
    {
        x = ref.x;
        y = ref.y;
 
        //For analysis.
        count++;
        printf("CopyConstructor was called.[copy from 0x%08x to 0x%08x].\n",&ref, this);
    }
 
};
 
int Vector::count = 0;

然后在main()函数中写上调用代码:

Vector a, b;
Vector::init();
printf("\n--Test add() --\n");
Vector c = add(a, b);
printf("----Constructors were called %d times. ----\n\n\n",Vector::count);

使用cl编译。

(注:Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86, Microsoft (R) Incremental Linker Version 10.00.40219.01)

完成后,运行程序,得到如下结果:

--
 Test add() --
Default
 Constructor was called.[0x0012fef8]
Copy
 Constructor was called.[copy from 0x0012fef8 to 0x0012ff60].
----
 Constructors were called 2 times.
 ----

 

由此可知,在没有优化的情况下,整个调用过程共创建了两个对象:

即:c和__temp0.

 

整个调用过程伪代码如下:

首先add()函数被编译器看做:

void add(Vector& __result, Vector& a, Vector & b)
{
    __result.x = a.x + b.x;
    __result.y = a.y + b.y;
    return;
}
而调用代码同时被修改为

Vector a, b;
Vector::init();
printf("\n--Test add() --\n");
Vector __temp0;     //构造函数.
add(__temp0,a, b);
Vector c(__temp0);  //拷贝构造函数.
printf("----Constructors were called %d times. ----\n\n\n", Vector::count);

现在就可以理解输出结果了吧。

这里要强调一点,看到”=”并不等于调用了Operator=()的代码,以下三种情况其实是等效的,都只调用了拷贝构造函数:

Vector b(a);
Vector b = a;
Vector b = Vector(a);

最精彩的部分在于,如果你用

cl
/Ox

编译代码,使优化达到最大,再次运行,得到如下结果:

--
 Test add() --
Default
 Constructor was called.[0x0012ff74]
----
 Constructors were called 1 times.
 ----

这次,只调用了默认构造函数。这样的修改被称作Named Return Value(NRV) Optimization。

什么是NRV优化呢,顾名思义,就是保存返回值的变量不再使用没名没姓的__temp0这样的东西了,而是直接把c作为返回变量,因此应该将NRV翻译为“有名字的返回变量”吧,侯捷翻译的《深入探索C++对象模型》居然把它称为“具名数值”,真是不知所云。

言归正传,NVR优化的伪代码如下:

Vector c;
add(c,a, b);
NVR优化的最大好处就是不会再去调用那次多余拷贝构造函数了(把__temp0拷贝到c) ,因此《深入探索C++对象模型》67页最下面才会说第一版没有拷贝构造函数,所以不能进行优化。其实是指优化的意义不大,或者说没有什么可优化的。
这是经过优化后的add函数汇编代码:
//
 segment of function: Vector add(Vecotr &a, Vector &b)
0040117B 
 mov         edx,dword ptr [esp+1Ch] //edx = b.x
0040117F 
 add         edx,dword ptr [esp+14h] //edx = edx + a.x
00401183 
 mov         ecx,dword ptr [esp+20h] //ecx = b.y
00401187 
 add         ecx,dword ptr [esp+18h] //ecx = ecx + a.y
0040118B 
 mov         dword ptr [esp+24h],edx //c.x = edx
0040118F 
 mov         edx,dword ptr ds:[40BDC0h]    //inline - printf arguments
00401195 
 push        edx                           //still arguments
00401196 
 push        4081E8h                       //arguments
0040119B 
 mov         dword ptr [esp+30h],ecx //c.y = ecx
0040119F 
 call        004011AE                      //call printf()
004011A4 
 add         esp,10h //function return routine.
004011A7 
 xor         eax,eax 
004011A9 
 pop         esi  
004011AA 
 add         esp,28h 
004011AD 
 ret

(注意:我尝试把Vector的拷贝构造函数删掉,同样生成了上面这段代码(一个字节都没变),因此我推测,拷贝构造函数并不是触发NRV优化的条件了,Lippman的书可能有点过时了。)

但是这样带来的坏处是,如果你在拷贝构造函数里面放上与拷贝无关的代码,比如我放入的printf和count++,那么这些东西就不会被调用了,产生优化前后代码不一致问题。所以大家要在此注意一下。













【C/C++】C++返回值优化:RVO与NRVO全解析 RVO全面位解析 阅读详情

相关推荐

基于PWM的汽车仪表步进电机微步驱动优化实践

本文深入探讨了基于PWM的微步驱动技术,用于优化汽车仪表步进电机的运行性能。通过将传统的分步驱动升级为PWM微步细分驱动,利用正弦/余弦查表法合成旋转磁场,有效消除了指针抖动与噪声,实现了指针的平滑、精准运动。文章以STM32和VID2905电机为例,提供了详细的硬件连接、代码实现及高级优化技巧,为提升汽车仪表体验提供了实用方案。

weixin_29169505的博客 512

NRV优化

一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义: class X; X bar() { X x1; // 处理 x1.. return x1; } 在学习C++语法时,我们知道了。针对”Xbar()”这样的函数,是返回class X的一个对象的拷贝。其返回值是一个对象,比如叫做x2。在执行return时,x2通过调用拷贝构造函数,拷贝对象x1来实现其

斯瓦馨阁 807

手把手教你用示波器抓取MIPI D-PHY波形:从LP11到HS Burst的完整信号解析

本文详细介绍了使用示波器精准捕获MIPI D-PHY信号的关键技巧,包括探头选择、示波器参数设置、信号状态解析及高级触发配置。通过实战案例和波形分析,帮助工程师快速识别从LP11到HS Burst的各种信号状态,解决常见的信号完整性问题,提升调试效率。

weixin_30471561的博客 561

GCC编译器下NRV优化

关于NRV优化的相关测试以及思考

weixin_46715151的博客 973

保姆级教程:DBeaver社区版安装与驱动配置(附阿里云镜像解决下载超时)

本文提供DBeaver社区版的保姆级安装教程,涵盖版本选择、安装路径设置到驱动配置优化。特别针对国内用户遇到的下载超时问题,详细介绍了如何配置阿里云镜像加速驱动下载,确保高效连接MySQL等主流数据库。

weixin_30430169的博客 479

python k线顶分型_【缠论】分型、笔的定义及其程序化

K线包含关系处理和分型的定义如果一个K线的高低点全在另一K线的范围里,称之为包含关系。针对这种K线,需要进行一些处理,去除包含关系,处理方法:向上时,把两K线的高点当高点,而两K线低点中的较高者当成低点;反之,当向下时,把两K线的低点当低点,而两K线高点中的较低者当成高点,这样就把两K线合并成一新的K线。经过这样的处理,所有K线图都可以处理成没有包含关系的图形。两K线的包含关系,可以分成四种:1)...

weixin_39717152的博客 2824

理解NRV优化

大纲:  函数返回局部对象的拷贝的一般实现方式。 NRV(Named Return Value)优化。 NRV优化触发的疑问。 一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义: class X; X bar() { X x1; // 处理 x1.. return x1; } 在学习C++语法时,我们知道了。针对”Xbar()”这样的函数

麻辣丝瓜的世界 7721

UVM实战 卷I学习笔记1——简单的UVM验证平台:只有driver

目录验证平台的组成最简单的验证平台——只有driverUVM如何搭建driver:加入factory机制加入objection机制加入virtual interface 个人《UVM实战卷I》学习随手笔记,此书作者:张强 验证平台的组成 一个验证平台要实现的基本功能: 模拟DUT的各种真实使用情况——给DUT施加各种激励→driver实现激励功能; 根据DUT的输出判断DUT行为是否与预期符合→scoreboard(也称为checker)——需注意判断的东西和判断的标准是什么; 收集DUT的输出并传递

weixin_41979380的博客 3890

[C++] NRV优化

对于函数返回对象时,编译器的NVR优化。

追寻梦想的青春 3180

《AI编程实战:深度学习模型改进与系统部署》,改进专栏目录和介绍

专深度学习相关项目都写在本专栏,《一次订阅解锁全部,都有源码,永久观看》,包括车牌识别、股票预测、人脸识别、风格迁移、图像修复、图像超分辨重建、图像分割等任务,Web/PyQt系统实现。引入Transformer、Swin Transformer、DenseNet、ViT、ConvNeXt等算法实战,提供卷积改进,魔改对比实验,模型剪枝,模型蒸馏,模型量化,全网独家整合改进专栏,为不同领域的同学发表论文提供了大力支持。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 3268

AI上推荐 之 NeuralCF与PNN模型(改变特征交叉方式)

1. 前言 随着信息技术和互联网的发展, 我们已经步入了一个信息过载的时代,这个时代,无论是信息消费者还是信息生产者都遇到了很大的挑战: 信息消费者:如何从大量的信息中找到自己感兴趣的信息? 信息生产者:如何让自己生产的信息脱颖而出, 受到广大用户的关注? 为了解决这个矛盾, 推荐系统应时而生, 并飞速前进,在用户和信息之间架起了一道桥梁,一方面帮助用户发现对自己有价值的信息, 一方面让信息能够展现在对它感兴趣的用户前面。 推荐系统近几年有了深度学习的助推发展之势迅猛, 从前深度学习的传统推荐模型(协

Miracle8070 1万+

解释:《深度探索C++对象模型》对NRV优化的讨论

原文地址:http://blog.youkuaiyun.com/zha_1525515/article/details/7170059 感谢作者! 大纲:  函数返回局部对象的拷贝的一般实现方式。 NRV(Named Return Value)优化。 NRV优化触发的疑问。 一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义:

hujing_Liu 2105

高精地图构建:Lidar SLAM与视觉SLAM对比

高精地图构建:Lidar SLAM与视觉SLAM对比​ ,人工智能,计算机视觉,大模型,AI,本文围绕高精地图构建,对比 Lidar SLAM 与视觉 SLAM。Lidar SLAM 通过激光获取点云数据,精度高、环境适应性强但成本高、数据量大;视觉 SLAM 利用图像,成本低、信息丰富却受光照影响大、深度获取难。还解析了 SLAM、点云等关键概念,介绍了两者融合趋势、应用案例、技术挑战及未来方向,也给出常见问题解答,为相关领域应用与学习提供参考。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 17万+

编译器构造函数优化(NRV 优化)

NRV优化 NRV优化指发生在 函数以值返回时的拷贝构造的优化。如下这个demo,函数以值函数会发生拷贝构造,但是某些场景这个拷贝构造可以被省去。 class T { }; T Fun(); 如下面这个场景发生了俩个构造,一个是默认构造函数构造出了ret,一个ret构造出了返回的临时对象。 class T { public: T() {} T(const& T)...

我喜欢雨天的清新的博客 838

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

拷贝构造函数与NRV优化

《深度探索C++对象模型》第二章67页有: 这个程序的第一个版本不能实施NRV优化,因为test class 缺少一个copy constructor. 也就是缺少拷贝构造函数所以不能NRV优化。 NRV优化和拷贝构造函数是有关系的,只有定义了拷贝构造函数才会开启NRV优化,但现代编译器NRV优化的开启一般都与拷贝构造函数没有关系, 早期的 cfront需要一个开关来决定是否应该对代码实行...

speargod的博客 379

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

NRV优化详解

原文地址 http://blog.youkuaiyun.com/zha_1525515/article/details/7170059 大纲:  函数返回局部对象的拷贝的一般实现方式。 NRV(Named Return Value)优化。 NRV优化触发的疑问。 一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义: [c

不积跬步无以至千里 1534

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

NRV

这里举一个NRV优化的例子。 一般有copy constructor会NRV优化。现在没有这个规定,根据编译器各有不同。没有copy constructor 使用bitwise copy 效率也很高效。

Hiker的专栏 994

nrv优化

原文地址 http://blog.youkuaiyun.com/zha_1525515/article/details/7170059 大纲:  函数返回局部对象的拷贝的一般实现方式。 NRV(Named Return Value)优化。 NRV优化触发的疑问。 一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义: [cpp] view p

wangkaiblog的专栏 653

C++对象模型的那些事儿之五:NRV优化和初始化列表

前言在C++对象模型的那些事儿之四:拷贝构造函数中提到如果将一个对象作为函数参数或者返回值的时候,会调用拷贝构造函数,编译器是如何处理这些步骤,又会对其做哪些优化呢?本篇博客就为他家介绍一个编译器的优化操作:NRV,以及关于初始化列表的一些容易踩的“坑”!NRVNRV是named Return Value的缩写,翻译过来就是具名返回值优化,这个优化到底在编译层干了些什么事,我们先来看个例子:clas

ZeeCoder 876

第二章构造函数语义学--关于NRV优化和copy constructor

《深度探索C++对象模型》P67最下面说道: 这个程序的第一个版本不能实施NRV优化,因为test class 缺少一个copy constructor. 看了一下程序,这个NRV优化和copy constructor木有关系的说啊,所以为什么没有copy constructor就不能进行NRV优化了呢。 搜了一下,结果很坑爹,就是 侯捷也不知道,Lippman也没有给出答案,而且...

aiyun1242的博客 303

【转】关于RVO与NRV

 关于RVO与NRV  RVO:Return Value Optimization这种特殊的优化——通过使用函数的return位置(或者在函数被调用位置用一个对象来替代)来消除局部临时对象——是众所周知的和被普遍实现的。它甚至还有一个名字:返回值优化(译注:详见《深度探索C++对象模型》)。实际上,这种优化有自己的名字本身就可以解释为什么它被广泛地使用。(转自《More Effec

ckt1120的专栏 1273

NRV优化所带来的困惑

我们知道要了解编译器在做什么,NRV优化应该是一个无法避免的问题,下面来看一个例子 #include  "iostream"             //从这两个程序的运行来看NRV优化好像并不是那么如你想象中的好 using   namespace std; #include class text { friend text foo(double); public : text

u013197391的专栏 416

快速检验NRV优化测试代码

在读《深度探索C++对象模型》一书shi

u012329294的专栏 725
上一篇: 《深度探索C++对象模型》读书笔记第二章:构造函数语意学
下一篇: 《深度探索C++对象模型》读书笔记第三章:Data语意学
FreeeLinux
博客等级 码龄11年 421粉丝 356原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值