zoukankan      html  css  js  c++  java
  • More Effective C++ 条款20 协助完成"返回值优化(RVO)"

      1. 函数如果返回对象,就会产生临时对象(见条款19)的构造,析构等过程,考虑以下重载的operator*:

    class Rational{
    public:
        int getNumerator(){return numerator;}
        int getDenomerator(){return denominator;}
        Rational(int numerator,int denomimator);
        ...
    privateint numerator;
        int denominator;
    }
    Rational operator*(const Rational& lhs,const Rational& rhs){
        Rational result(lhs.getNumerator()*rhs.getNumerator()+lhs.getDenominator()+rhs.getDenominator());
        return result;//result是Rational类型的对象,用于存储结果
    }
    View Code

        由于operator*要返回一个Rational对象,那么就涉及到临时对象的构造和析构问题:调用operator*时,编译器需要构造一个临时的Rational对象用于存储result的内容(因为程序一operator*的作用域result就被销毁),相应的也需要适时销毁该临时对象,这就导致了额外的成本.

        令函数返回指针来消除临时对象的方法是行不通的.实际上,令函数返回指针或引用是一个不好的习惯:如果返回的指针指向的是函数内创建的non-static对象,那么当离开函数作用域时,该对象就被销毁,任何企图通过指针访问其指向的内存的行为都会导致程序错误;如果函数返回的是heap对象,那么就增加了额外的手动释放内存的负担.因此应尽量避免令函数返回指针.返回引用的缺点类似.

    2. 大多数编译器具有一种优化方法——RVO(return value optimization,返回值优化),如果函数返回匿名对象,那么函数就有可能避免临时对象的构造,也就是说,如果operator*的实现像这样:

    Rational operator*(const Rational&lhs,const Rational&rhs){
        return Rational(lhs.getNumerator()*rhs.getNumerator() +lhs.getDenominator()+rhs.getDenominator());
    }
    View Code

        函数返回一个临时对象就传达给编译器这样一个信息:允许编译器在合适的时候采取RVO优化将消除临时对象的构造和析构成本,于是像这样的调用:

    Rational c=a*b;//a和b是Rational型对象

        编译器可以直接在c上构造a和b相乘的结果而不需要先将结果存储在一临时对象中,然后再拷贝给a(方法可能是将a作为参数传入operator*).这就是RVO:返回值优化.

    3. 要实现RVO,编译器通常要求函数返回匿名对象,新式的编译器支持其进化版——NRV(named return value),它可以对具名返回值做优化,消除构造和析构临时对象的成本,也就是说,即使对第一种operator*实现版本,支持NRV的编译器仍然可以对于以下语句实行优化,消除临时对象的构造和析构:

    Rational c=a*b;//a和b是Rational型对象

        据说vc 8.0以上的版本支持NRV优化(http://www.cnitblog.com/luckydmz/archive/2011/10/25/76193.html)

        其实RVO要求函数返回匿名对象是有理由的:如果函数返回匿名对象,那么就说明程序员只是利用该匿名对象存储返回值,而并不打算用该匿名对象做其他事情,因此编译器可以将其视为一个允许它做RVO优化的信号,而NRV优化虽然可以对具名返回值做优化,以消除匿名对象的成本,这提高了效率,但有可能是不是程序员的原意.

        对于NRV优化的深入了解见《深入探索C++面向对象模型》第二章

    4. C++11引入了右值引用与转移语义,其目的不是消除临时对象的构造和析构,而是通过右值引用绑定到右值来延长临时对象生存期,从而直接使用临时对象,这种方法与RVO异曲同工:前者是直接使用临时对象,不再构造新对象;后者避免构造临时对象,将本应构造在临时对象上的内容直接构造到新对象上.

  • 相关阅读:
    C# 语音识别
    Android—监听器
    android-Activity
    Android小案例——简单图片浏览器
    针对Android 模拟器启动慢的问题
    emulator-arm.exe 已停止工作、 emulator-x86 已停止工作
    android模拟器启动没有拨号功能
    安装好android的adt以后重启eclipse,但是没有创建AVD的图标
    android环境搭建——工欲善其事,必先利其器 2
    SQL 中常见的系统存储过程
  • 原文地址:https://www.cnblogs.com/reasno/p/4833696.html
Copyright © 2011-2022 走看看