zoukankan      html  css  js  c++  java
  • 20. 协助完成“返回值优化(RVO)”

    [19]最后曾提到了在函数通过传值方式(by value)返回一个对象时,不可避免地要生成一个临时对象,这会严重影响到程序的效率,如下例计算两个分式的乘积:

    
    
    class CRational{
    public:
        CRational(int numerator, int denominator)
        {
            this->numerator = numerator;
            this->denominator = denominator;
        }
        int numer() const        //get numerator
        {
            return numerator;
        }
        int denom() const       //get denominator
        {
            return denominator;
        }
    private:
        int numerator;
        int denominator;
    };
    const CRational operator *(const CRational& lhs, const CRational& rhs)
    {
        CRational res(lhs.numer() * rhs.numer(), lhs.denom() * rhs.denom());
        return res;
    } 
    CRational a(1, 3);
    CRational b(2, 3);
    CRational c = a * b;     // 调用函数 operator *()

    我们来仔细分析一下operator * 完成的功能,生成一个局部对象res ,调用构造函数进行了初始化,函数返回时,还会生成一个临时变量,用res进行copy constructor,返回之后会销毁res对象,而调用c = a * b; 时将临时对象用来初始化c,然后再销毁这个临时变量。

    上面这一系列的构造、析构对象,严重影响了程序的性能,那么有什么办法可以消除这种影响呢?

    能否通过返回一个对象指针呢?将实例的函数改为:

    
    
    const CRational* operator *(const CRational& lhs, const CRational& rhs)
    {
        CRational *pres = 
                  new CRational(lhs.numer() * rhs.numer(), lhs.denom() * rhs.denom());
        return pres;
    }
    CRational c = * (a * b);    //调用函数

    暂不说最后调用函数的表达式看起来很不自然,最严重的问题是这样写容易导致内存泄漏,因为我们往往会忘记释放函数返回来的指针。

    能否通过返回对象的reference呢?于是上面的实例的函数被修改为:

    const CRational& operator *(const CRational& lhs, const CRational& rhs)
    {
        CRational res(lhs.numer() * rhs.numer(), lhs.denom() * rhs.denom());
        return res;
    }
    CRational c = a * b;           //看起来没啥问题对吧?

    貌似这种做法,可以不用生成临时对象,因为返回的是对象的引用,直接指向res。事实上,这种做法是错误的!函数返回的是reference,指向一个局部对象,而局部对象在函数返回时是要被释放的,因此res在operator *返回时已经不存在了,所以这种做法是不被编译器允许的!

    额...貌似没什么其他办法消除返回的临时对象了,那么能否通过其他方式让编译器消除生成临时对象的成本呢?

    我们的做法是:返回constructor arguments 取代局部对象!如下:

    const CRational operator *(const CRational& lhs, const CRational& rhs)
    {
        return res(lhs.numer() * rhs.numer(), lhs.denom() * rhs.denom());
    }
    
    

    看起来和最开始的做法没什么区别吧....因为还是要生成函数内部临时对象已经函数返回临时对象!但是,这种做法下C++允许编译器将临时对象进行优化,使它们不存在。如调用

    CRational c = a * b;

    临时对象构造与c的内存内,这样整个过程,你只需付出一个constructor(用来生成c)的代价,而并没有函数内部和返回时临时对象的构造和析构的代价了。

    如果将函数再定义为内联函数,将又会节省函数调用的成本。下面是一个最有效的做法:

    
    
    inline const CRational operator *(const CRational& lhs, const CRational& rhs)
    {
        return res(lhs.numer() * rhs.numer(), lhs.denom() * rhs.denom());    
    }
    CRational c = a * b;
    //与下面的语句几乎具有相同的代价
    CRational c(a.numer() * b.numer(), a.denom() * b.denom());
    
    

    而编译器这种优化行为,被称为"Return Value Optimization"(RVO)!

  • 相关阅读:
    Solr 配置连接数据库
    最大利润
    分割金条的最小代价
    民居点亮
    一个会议室最多安排几场宣讲
    N皇后问题
    Integer的缓存机制
    Windows快捷键
    二叉树中两个节点的最低公共祖节点
    判断二叉树是不是完全二叉树
  • 原文地址:https://www.cnblogs.com/hazir/p/2456840.html
Copyright © 2011-2022 走看看