More Effective C++ 条款22 考虑以操作符复合形式(op=)取代其独身形式(op)
1. 一般来说,重载了算数操作符(以下称"独身形式"),那么也就要重载复合赋值操作符(以下称"复合形式").要确保操作符的复合形式例如(operator+=)和独身形式(例如operator+)行为相一致,基于前者实现后者是一个好方法.例如:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class Rational{ public: Rational operator+=(const Rational&); ... } Rational operator+(const T&lhs,const T&rhs){ return T(lhs)+=rhs; }
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)反而更能帮助编译器实现优化:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Rational operator+(const Rational&lhs,const Ration&rhs){ Rational temp(lhs); temp+=rhs; return temp; }
以下是实验代码以及Win7-32,Visual Studio 2013 release模式下的结果:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }
运行结果为
共调用了两次copy constructor,可见并没有实行优化.
operator+改为实现2后:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Rational operator+(const Rational& lhs, const Rational& rhs){ Rational temp(lhs); temp += rhs; return temp; }
实验结果为:
只调用了一次copy constructor,可见直接将operator+中的temp替换为外层的c,即实行了NRVO优化.
以上实验的详细解释可参照http://www.xuebuyuan.com/1595871.html,由于实验平台不同,实验结果会有出入,但是中心思想是相同的.