C++ 命名返回值优化(NRVO)

2023-11-04

命名的返回值优化(NRVO),这优化了冗余拷贝构造函数和析构函数调用,从而提高了总体性能。值得注意的是,这可能导致优化和非优化程序之间的不同行为。

下面是代码段1中的一个简单示例,以说明优化及其实现方式:

A MyMethod (B &var)
{
    A retVal;
    retVal.member = var.value + bar(var);
    return retVal;
}

使用上述函数的程序可能具有如下构造:

valA = MyMethod(valB);

从MyMethod返回的值是在valA通过使用隐藏的参数所指向的内存空间中创建的。下面是当我们公开隐藏的参数并显示地显示构造函数和析构函数时的功能:

A MyMethod(A &_hiddenArg, B &var)
{
    A retVal;
    retVal.A::A(); //constructor for retVal
    retVal.member = var.value + var(var);
    _hiddenArg.A::A(retVal); // the copy constructor for A
    return;
return.A::~A(); // destructor for retVal
}

上段代码为不使用NRVO的隐藏参数代码(伪代码)
从上面的代码可以看出,有一些优化的机会。其基本思想是消除基于堆栈的临时值(retVal)并使用隐藏的参数。因此,这将消除基于堆栈的值的拷贝构造函数和析构函数。下面是基于NRVO的优化代码:

A MyMethod(A &_hiddenArg, B &var)
{
    _hiddenArg.A::A();
    _hiddenArg.member = var.value + bar(var);
    Resurn
}

带有NRVO的隐藏参数代码(伪代码)

代码示例
示例1:简单示例

#include <stdio.h>

class RVO
{
public:
        RVO() { printf("I am in constructor\n"); }
        RVO( const RVO& c_RVO ) { printf("I am in copy constructor\n"); }
        ~RVO() { printf("I am in destructor\n"); }
        int mem_var;
};

RVO MyMethod(int i)
{
        RVO rvo;
        rvo.mem_var = i;
        return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod(5);
}

代码:Sample1.cpp
如果没有NRVO,预期的输出将是:

I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor

使用NRVO,预期的输出将是:

I am in constructor
I am in constructor
I am in destructor
I am in destructor

示例2:更复杂的示例

#include <stdio.h>
class A
{
public:
        A()
        {
                printf("A: I am in constructor\n");
                i = 1;
        }

        ~A()
        {
                printf("A: I am in destructor\n");
                i = 0;
        }

        A(const A& a)
        {
                printf("A: I am in copy constructor\n");
                i = a.i;
        }

        int i, x, w;
};

class B
{
public:
        A a;
        B()
        {
                printf("B: I am in constructor\n");
        }

        ~B()
        {
                printf("B: I am in destructor\n");
        }

        B(const B& b)
        {
                printf("B: I am in copy constructor\n");
        }
};

A MyMethod()
{
        B* b = new B();
        A a = b->a;
        delete b;
        return a;
}

int main()
{
        A a;
        a = MyMethod();
}

代码Sample2.cpp
无NRVO的输出将如下所:

A: I am in constructor
A: I am in constructor
B: I am in constructor
A: I am in copy constructor
B: I am in destructor
A: I am in destructor
A: I am in copy constructor
A: I am in destructor
A: I am in destructor
A: I am in destructor

当NRVO优化启动时,输出将是:

A: I am in constructor
A: I am in constructor
B: I am in constructor
A: I am in copy constructor
B: I am in destructor
A: I am in destructor
A: I am in destructor
A: I am in destructor

优化限制
有些情况下,优化不会真正启动。以下是这些限制的样本
示例3:异常示例
在遇到异常时,隐藏的参数必须在它正在替换的临时范围内被破坏。

// RVO class is defined above in figure 4
#include <stdio.h>
RVO MyMethod(int i)
{
    RVO rvo;
    cvo.mem_var = i;
    throw "I am throwing an exception!";
    return rvo;
}

int main()
{
    RVO rvo;
    try
    {
        rvo = MyMethod(5);
    }
    catch(char* str)
    {
        printf("I caught the exception\n");
    }

代码Sample3.cpp
如果没有NRVO,预期的输出将是:

I am in constructor
I am in constructor
I am in destructor
I caught the exception
I am in destructor

如果“抛出”被注释掉,输出将是:

I am in constructor
I am in constructor
I am in copy constructor
I am in destructor
I am in destructor
I am in destructor

现在,如果“抛出”被注释掉,并且NRVO被触发,输出将如下所示:

I am in constructor
I am in constructor
I am in destructor
I am in destructor

也就是说sample3.cpp在没有NRVO的情况下,会表现出相同的行为。

示例4:不同的命名对象示例
若要使用优化,所有退出路径必须返回同一命名对象。

#include <stdio.h>
class RVO
{
public:
        RVO()
        {
                printf("I am in construct\n");
        }

        RVO(const RVO& c_RVO)
        {
                printf("I am in copy construct\n");
        }

        int mem_var;
};

RVO MyMethod(int i)
{
        RVO rvo;
        rvo.mem_var = i;
        if( rvo.mem_var == 10 )
                return RVO();
        return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod(5);
}

代码Sample4.cpp
启用优化时输出与不启用任何优化相同。NRVO实际上并不发生,因为并非所有返回都返回相同的对象。

I am in constructor
I am in constructor
I am in copy constructor

如果将上面的示例更改为返回rvo。在返回对象时,优化将消除复制构造函数:

#include <stdio.h>
class RVO
{
public:
        RVO()
        {
                printf("I am in constructor\n");
        }

        RVO(const RVO& c_RVO)
        {
                printf("I am in copy constructor\n");
        }

        int mem_var;
};

RVO MyMethod(int i)
{
        RVO rvo;
        if( i == 10 )
                return rvo;
        rvo.mem_var = i;
                return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod(5);
}

代码Sample4_Modified.cpp修改并使用NRVO,输出结果将如下所示:

I am in constructor
I am in constructor

优化副作用
程序员应该意识到这种优化可能会影响应用程序的流程。下面的示例说明了这种副作用:

#include <stdio.h>

int NumConsCalls = 0;
int NumCpyConsCalls = 0;

class RVO
{
public:
        RVO()
        {
                NumConsCalls ++;
        }

        RVO(const RVO& c_RVO)
        {
                NumCpyConsCalls++;
        }
};

RVO MyMethod()
{
        RVO rvo;
        return rvo;
}

int main()
{
        RVO rvo;
        rvo = MyMethod();
        int Division = NumConsCalls / NumCpyConsCalls;
        printf("Construct calls / Copy constructor calls = %d\n", Division);
}

代码段Sample5.cpp
编译未启用优化将产生大多数用户所期望的。“构造函数”被调用两次。“拷贝构造函数”被调用一次。因此除法生成2。

Constructor calls / Copy constructor calls = 2

另一方面,如果上面的代码通过启用优化进行编译,NRVO将会启用。因此“拷贝构造函数”调用将被删除。因此,NumCpyConsCalls将为零,导致异常。如果没有适当处理,可以导致应用程序崩溃。


引入自:https://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx


 本文转自 XDATAPLUS 51CTO博客,原文链接:http://blog.51cto.com/xdataplus/2069825


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 命名返回值优化(NRVO) 的相关文章

  • QCC300x笔记(6) -- QCC3007的OTA流程梳理

    哈喽大家好 这是该系列博文的第六篇 篇 lt lt 系列博文索引 快速通道 gt gt 写在前面 QCC300x支持OTA功能 官方提供了详细的文档说明 按照文档即可升级成功 下面是对流程的简单梳理 可参考 文档和APP下载 1 生产APP

随机推荐

  • C语言解析FLM(ELF)格式文件

    C语言解析FLM ELF 格式文件 代码下载 https gitee com jhembedded flmparse 1 前言 写这篇博客的目的是因为最近在做一个STM32的离线编程器 离线下载需要用到FLM文件的下载算法 所以实现了一下提
  • [人工智能-深度学习-28]:卷积神经网络CNN - 网络架构与描述方法

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 120806599 目录 第1章 卷积神
  • Spring Boot Event Bus用法

    Spring Boot Event Bus是Spring框架中事件驱动编程的一部分 它为应用程序中的不同组件提供了一种解耦的方式 以便它们可以相互通信和交互 以下是Spring Boot Event Bus的用法 导入依赖 首先 您需要在项
  • 如何编写脚本对项目代码进行分析(测试篇)

    本篇将从一个测试的角度 对于在开发过程中 如何对代码进行完善的测试 包括功能以及性能的测试 内容属于杂谈 可能不够严谨 但希望能帮到看到这篇文章的各位 前言 我想没有人会质疑测试的重要性 我们自己编写的代码 可能需要经过很多次的测试才能上线
  • Java 数据库介绍

    数据库基础 数据库概念 数据库 Database 是按照数据结构来组织 存储和管理数据的仓库 它是存储数据集合的容器 可以看做一个文件夹 里面存放着一系列有关联的数据表 数据表 Table 是一种结构化的数据 以列和行为单位来存储的一种结构
  • 基于TCP协议的select多路复用IO服务器编程

    一 初识TCP协议 TCP协议是面向链接 可靠的 基于字节流传输层协议 使用严格的应答机制来保证可靠性 1 建立连接时进行三次握手 2 断开连接时进行四次挥手 3 每次发送数据后 都必须要应答 4 会给每个数据包编号 应答时区分编号 TCP
  • Visual Studio 跨平台开发实战(5) - Xamarin Android 多页面应用程式开发

    前言 大部份的Android 都具有实体或虚拟的Back鍵 因此在处理多页面应用程式时 与先前所介绍的iOS Navigation controller 比较起来会简单许多 1 开启Visual Studio 并新增Android Appl
  • Python爬虫到底要学到什么程度才能接单赚钱呢

    Python爬虫可以做副业接单 一些个人或者企业想要爬一些资料数据之类的 可以给他们爬 费用几百上千不等 这又可以增加个人的收入来源 Python爬虫学到什么程度可以接单 你得要熟练使用Python爬虫 那么一些Python基础知识肯定需要
  • OpenGL计算着色器实现光线追踪——以球体跟踪为例

    OpenGL计算着色器实现光线追踪 以球体跟踪为例 光线追踪是渲染领域中的一种技术 通过在场景中发射光线并迭代计算来确定每个像素的颜色值 这种技术可以用于生成真实感和高度逼真的渲染图像 而在OpenGL中 我们可以利用计算着色器实现光线追踪
  • Qt应用开发(基础篇)——工具按钮类 QToolButton

    一 前言 QToolButton类继承于QAbstractButton 该部件为命令或选项提供了一个快速访问按钮 通常用于QToolBar中 按钮基类 QAbstractButton QToolButton是一个特殊的按钮 一般显示文本 只
  • 机器学习中的高斯分布

    文章目录 一 高斯分布的概率密度函数 二 一元高斯分布的极大似然估计 2 1 M L E
  • box2d 服务器性能,Box2d三种施加力的方法

    package import Box2D Collision Shapes b2PolygonShape import Box2D Common Math b2Vec2 import Box2D Dynamics Joints b2Revo
  • 2023中国新型灵活就业报告

    导读 9月12日 暨南大学经济与社会研究院和智联招聘联合发布 2023中国新型灵活就业报告 据了解 本报告中新型灵活就业职位具体包括八类工种 平台电商 生活配送 生活服务 平台微商 知识服务 自媒体 平台直播 共享出行司机 八类工种中生活配
  • 测试边界值(上点、内点、离点)

    测试边界值 上点 内点 离点 上点 就是指得边界上得点 开区间的话 上点就是在域外 闭区间得话 上点就是在域内 离点 指得就是离上点最近得点 如果是开区间 那么离点就在域内 如果是闭区间 那么离点就在域外 内点 域内得任意点都是内点 实例
  • scala学习系列(四) Scala关键字(持续更新)

    Scala有39个关键字 package import class object 伴生对象关键字 trait extends with type for private protected abstract sealed final imp
  • [Unity]环形进度条(Progress)/拖拽条(Slider)制作

    先上效果图 上图演示效果可用于圆形进度条的加载 或者用于拖拽验证码的实现 原理相同 以下所有算法获得的坐标均是在fillorign为top时的公式 拖拽物体的位置 通过点击拖拽获取当前Rect下本地坐标 然后将这个坐标进行标准化 norma
  • C++一行输入多个整数,每个整数用空格隔开,回车结束输入

    C 一行输入多个整数 每个整数用空格隔开 回车结束输入 include
  • 求生之路2社区服务器sourcemod安装配置搭建教程centos

    求生之路2社区服务器sourcemod安装配置搭建教程centos 大家好我是艾西 通过上文我们已经成功搭建了求生之路2的服务端 但是这个服务端是纯净的服务端 就是那种最纯粹的原版 如果想要实现插件 sm开头的命令等功能 需要安装这个sou
  • QZXing识别二维码

    下载QZXing这个识别二维码库 在github上下载qzxing https github com zxing zxing中的QZXing 新建qt工程 在pro文件中加入include QZXing sourceV2 4 QZXing
  • C++ 命名返回值优化(NRVO)

    命名的返回值优化 NRVO 这优化了冗余拷贝构造函数和析构函数调用 从而提高了总体性能 值得注意的是 这可能导致优化和非优化程序之间的不同行为 下面是代码段1中的一个简单示例 以说明优化及其实现方式 A MyMethod B var A r