More Effective C++ 条款22 考虑以操作符复合形式(op=)取代其独身形式(op)

1. 一般来说,重载了算数操作符(以下称"独身形式"),那么也就要重载复合赋值操作符(以下称"复合形式").要确保操作符的复合形式例如(operator+=)和独身形式(例如operator+)行为相一致,基于前者实现后者是一个好方法.例如:

class Rational{
public:
    Rational operator+=(const Rational&);
   ...
}
Rational operator+(const T&lhs,const T&rhs){
    return T(lhs)+=rhs;
}
View Code

 2. 操作符的复合形式通常比其独身形式效率更高:独身形式需要返回新对象,因而需要承担临时对象的构造和析构成本,复合形式直接将结果写入右端变量,不需要临时对象的构造过程,因此以下使用独身形式的写法:

Rational a,b,c,d;
...
Rational result=a+b+c+d;

    效率没有使用复合形式的写法高:

Rational a,b,c,d;
Rational result=a;
result+=b+=c+=d;

    因为前者使用operator+产生了3个临时对象,即使有RVO(返回值优化,见条款21),那也只是发生在最后一个operator+,而前两个operator+构造的临时对象不会被优化掉(稍后会解释,如果按照1中operator+的实现,即使是最后一个operator+产生的临时对象也不会被优化,因此临时对象总共有3个而不是2个).

    前者易编写,维护,调试而后者效率较高,对汇编程序员比较直观.

3. 《More Effective C++》解释了1中operator的实现方法为

Rational operator+(const T&lhs,const T&rhs){
    return T(lhs)+=rhs;
}

的原因(为了便于说明,称以上实现为版本1),声称返回匿名对象比返回具名对象更容易使编译器实行RVO,但所谓的"返回匿名对象比返回具名对象更容易使比编译器实行RVO"是建立在两个基础之上的:1.NRVO(具名返回值优化,见条款21)还未出现;2.返回匿名对象,return语句只返回一个匿名对象,而不再对其进行其他操作.

    对于以下操作:

Rational a,b;
Rational c=a+b;

    operator+的版本1实际上是无法完成RVO的:版本的最后一步是return T(lhs)+=rhs,返回的不是临时对象T(lhs),而是临时对象调用operator+=后的结果,此时编译器不知道谁才是要返回的结果(T(lhs)这个临时对象显然不是,因为还有后续操作),因此只能老老实实的对T(lhs)调用operator+=,再将临时对象的结果拷贝给c,临时对象的产生无法避免.

    也就是说,《More Effective C++》所提倡的这种方法现在来看已经不合时宜了,在NRVO已经普及的今天,operator+的以下实现(称版本2)反而更能帮助编译器实现优化:

Rational operator+(const Rational&lhs,const Ration&rhs){
    Rational temp(lhs);
    temp+=rhs;
    return temp;
}
View Code

     以下是实验代码以及Win7-32,Visual Studio 2013 release模式下的结果:

#include<iostream>
using namespace std;
class Rational{
public:
    Rational& operator+=(const Rational&rhs){
        numerator += rhs.numerator;
        denominator += rhs.denominator;
        return *this;
    }
    Rational(const Rational&rhs){
        cout << "copy constructor" << endl;
    }
    Rational() :numerator(0), denominator(0){ cout << "constructor" << endl; }
private:
    int numerator;
    int denominator;
};
Rational operator+(const Rational& lhs, const Rational& rhs){
    return Rational(lhs)+=rhs;
}
int main(){
    Rational a, b;
    Rational c = a + b;
    system("pause");
    return 0;
}
View Code

    运行结果为

    共调用了两次copy constructor,可见并没有实行优化.

    operator+改为实现2后:

Rational operator+(const Rational& lhs, const Rational& rhs){
    Rational temp(lhs);
    temp += rhs;
    return temp;
}
View Code

    实验结果为:

    只调用了一次copy constructor,可见直接将operator+中的temp替换为外层的c,即实行了NRVO优化.

    以上实验的详细解释可参照http://www.xuebuyuan.com/1595871.html,由于实验平台不同,实验结果会有出入,但是中心思想是相同的.

posted @ 2015-09-25 20:31  Reasno  阅读(376)  评论(0编辑  收藏  举报