首页 > 编程语言 >C++ 返回值优化RVO

C++ 返回值优化RVO

时间:2023-01-08 23:45:00浏览次数:61  
标签:real const imag RVO C++ Complex operator 返回值

目录

返回值优化(Return Value Optimization, 简称RVO)是通过对源代码进行转换、消除对象的创建来实现加速程序,提升程序性能的优化措施,通常是由编译器实现的优化。

按值返回

假设我们打算实现Complex类,用于实现复数。可以这样设计Complex类:

class Complex
{
    // Complex addition operator
    friend Complex operator+(const Complex&, const Complex&);

public:
    // Default ctor
    Complex(double r = 0.0, double i = 0.0)
    : real(r), imag(i)
    {}

    // Copy ctor
    Complex(const Complex& c)
    : real(c.real), imag(c.imag)
    {}

    // Assignment operator
    Complex& operator=(const Complex& c);

    ~Complex(){}
private:
    double real;
    double imag;
};

我们是如何实现友元函数operator+(const Complex&, const Complex&),以实现两个复数相加的?
通常,我们这样写:

// 注意operator+是重载函数, 不是Complex类成员函数
Complex operator+(const Complex& a, const Complex& b)
{
    Complex retVal;
    retVal.real = a.real + b.real;
    retVal.imag = a.imag + b.imag;
    return retVal;
}

应用时,将Comlex对象c1, c2, c3按如下方式进行操作:

c3 = c1 + c2;

如何将c1+c2的值放到c3呢?
如果没有任何优化,调用运算符operator+时调用Complex构造函数,构造retVal对象;返回给c3时,又调用Complex的拷贝构造函数。也就是说,会构造两次Complex对象。

返回值优化

针对上面这种情形,编译器有专门的优化方式:编译器创建一个临时__result对象,作为一个专门的参数,按引用传递方式将结果传递给Complex::operator+()。
也就是说,编译器会把重载函数operator+重写为另一个稍有不同的函数:

void Complex_Add(const Complex& __result, const Complex& c1, const Complex& c2)
{
    ...
}

此时,原来的源代码c3 = c1 + c2,会被转换成(伪代码):

struct Complex __tempResult;         // 存储, 没有构造
Complex_Add(__tempResult, c1, c2);   // 所有参数引用传递
c3 = __tempResult;                   // 反馈结果给c3

这就是所谓返回值优化,编译器通过消除局部对象retVal并将其替换为__tempResult,优化Complex_Add()。

  • 编译器不执行RVO通常情形

然而,并不是所有编译器会对这种返回值是临时对象的函数,执行RVO。通常,下面两种情况下,编译器不执行RVO:
1)复杂函数;
2)函数有多个return语句,并且返回不同名称的对象。

  • 有一种编译器拒绝对以下特定版本operator+执行RVO
// operator+版本1
Complex operator+(const Complex& a, const Complex& b)
{
    Complex retVal;
    retVal.real = a.real + b.real;
    retVal.imag = a.imag + b.imag;
    return retVal;
}

// operator+版本2
Complex operator+(const Complex& a, const Complex& b)
{
    Complex retVal(a.real + b.real, a.imag + b.imag);
    return retVal;
}
  • 编译器会对以下版本使用RVO
// operator+版本3
Complex operator+(const Complex& a, const Complex& b)
{
    double r = a.real + b.real;
    double i = a.imag + b.imag;
    return Complex(r, i);
}

// operator+版本4
Complex operator+(const Complex& a, const Complex& b)
{
    return Complex(a.real + b.real, a.imag + b.imag);
}

区别在于版本1、2为临时对象使用了命名变量,版本3、4使用匿名变量。

如果想让编译器为命名变量执行RVO,可以在返回时使用std::move,将左值转换为右值。

计算性构造函数

编译器无法执行RVO时,可以用计算性构造函数。比如上面Complex例子中,我们可以实现版本5的operator+,在其中创建一个默认的匿名Complex对象并推迟成员变量的设置,随后对对象成员进行初始化。这个构造函数,就是计算性构造函数。

// operator+版本5
Complex operator+(const Complex& a, const Complex& b)
{
    return Complex(a, b);
}

// 计算性构造函数
Complex::Complex(const Complex& x, const Complex& y)
: real(x.real + y.real), imag(x.imag + y.imag)
{}

计算性构造函数缺点:必须为每种运算(如加、减、乘、除)符号,添加不同的计算性构造函数。

关闭RVO

可以通过编译选项-fno-elide-constructors,关闭RVO功能。

参考

[1]DovBulka, DavidMayhew, 布尔卡,等. 提高C++性能的编程技术[M]. 清华大学出版社, 2003.
[2]C++返回值拷贝以及std::move | CSDN

标签:real,const,imag,RVO,C++,Complex,operator,返回值
From: https://www.cnblogs.com/fortunely/p/17035769.html

相关文章

  • 【condition_variable】C++条件变量的原理和用法
    condition_variable简介condition_variable类是一个同步原语,与std::mutex一起使用,用于阻塞一个或多个线程,直到另一个线程修改一个共享变量(条件)并通知condition_variable。......
  • C++教学创新大赛信息管理系统[2023-01-08]
    C++教学创新大赛信息管理系统[2023-01-08]2022级《计算思维综合实践I》课程任务书及相关要求适用班级:计算机类2022级、大数据2022级、人工智能2022级一、课程目标1.【......
  • C++指针【cherno课程学习】
    定义:指针是一个整数,一种存储内存地址的数字内存就像一条线性的线,在这条街上的每一个房子都有一个号码和地址类似比喻成电脑,这条街上每一个房子的地址是一个字节我们......
  • 函数---函数参数和返回值
    函数---函数参数和返回值标签(空格分隔):python目录函数---函数参数和返回值1,函数1.1数学定义1.2Python函数1.3函数的作用1.4函数的分类2,函数定义3,函数调用4,函数参数4.1......
  • C++实现双向链表的相关操作代码
    #include<iostream>#include<cstdlib>usingnamespacestd;#defineOK1#defineERROR0#defineMAXSIZE100typedefintElemtype;typedefintStatus;typedefstructDuL......
  • c++ vector容器总结
    vector1.动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。(一般会找比预料更多的空间)2.vector容器构造1.构造vector​​<int>......
  • c++使用接口,通过纯虚函数实现
    #include<iostream>usingnamespacestd;classShape{protected:intwidth;intheight;stringname;public://purevirtialfunctionfori......
  • C++核心知识回顾(函数&参数、异常、动态分配)
    复习C++的核心知识函数与参数传值参数、模板函数、引用参数、常量引用参数传值参数intabc(inta,intb,intc){returna+b*c;}a、b、c是函数abc的形参,下......
  • c++ virtual关键字学习
    virtual在类中使用如在多继承中(环状继承):classD{......};classB:publicD{......};classA:publicD{......};classC:publicB,publicA{.....};这个继承......
  • C++ - 多线程
    1.多线程传统的C++(C++11之前)中并没有引入线程这个概念,在C++11出来之前,如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的<pthread.h>,或者windows下......