C ++中这些运算符重载方法的不同之处

时间:2013-06-17 19:46:38

标签: c++ operator-overloading

#include<iostream>
using namespace std;
class X
{
    int i;

    public:
    X(int a=0) : i(a) {}

    friend X operator+ (const X& left,const X&right);  

};
X operator+ (const X& left,const X&right)  // Method 1
{
    return X(left.i + right.i);
}

X operator+ (const X& left,const X&right) // Method 2
{
    X temp(left.i + right.i);
    return temp;
}

int main()
{
    X a(2),b(3),c;

    c=a+b;

    c.print();
    return 0;
}

在此代码中,Operator +通过2种不同的方法重载。

我的问题是这些方法之间有什么区别,应该考虑哪些方法更实用?

4 个答案:

答案 0 :(得分:6)

我看不出任何编译器会在这两个版本之间生成不同代码的情况。第二个稍微冗长一点,但在这种情况下允许编译器优化掉名义上的额外副本,我不知道任何编译器不会做那个省略。

那就是说,这是一个微优化:编写最清晰的代码,然后将我带到最后一点。不要编写这些运算符中的任何一个,而是将惯用版本与+=

结合使用
X& operator+=(const X&right) { i += right.i; return *this; }
X operator+(X left, const X& right) { return left += right; }

答案 1 :(得分:3)

这两种方法之间没有区别,您应该使用最符合您意图的方法。

copy elision 的第12.8 / 31段规定:

  

当满足某些条件时,允许实现省略类的复制/移动构造   即使为复制/移动操作选择的构造函数和/或对象的析构函数也是如此   有副作用。在这种情况下,实现处理省略的复制/移动的源和目标   操作只是指向同一对象的两种不同方式,以及对该对象的破坏   发生在两个对象在没有优化的情况下被销毁的时间的晚些时候。   复制/移动操作的省略,称为复制省略,在以下情况下是允许的(其中   可以合并以消除多个副本):

     

- 在具有类返回类型的函数的return语句中,当表达式是a的名称时   具有相同cv-unqualified的非易失性自动对象(函数或catch子句参数除外)   键入函数返回类型,可以通过构造省略复制/移动操作   自动对象直接进入函数的返回值

     

- [...]

正如您所看到的,创建临时和 id-expression 命名具有自动存储持续时间的本地对象都有资格进行复制省略。

此外,编译器会将本地temp视为 rvalue (读取:在本例中类似于临时),以便从函数返回它。 C ++ 11标准第12.8 / 32段规定:

  

当满足或将满足复制操作的省略标准时,除了源的事实   object是一个函数参数,要复制的对象由左值,重载决策指定   选择首先执行复制的构造函数,就好像对象是由右值指定的一样。 [...]

因此,我强烈建议从返回类型中移除 const限定条件:

    const X operator + (const X& left, const X&right)
//  ^^^^^
//  Don't use this!

在C ++ 11中,这将禁止移动语义,因为你不能从const对象移动,即使它是一个rvalue - 简而言之,X的移动构造函数,提供一个存在,不会被选中,并且将调用复制构造函数。

答案 2 :(得分:2)

区别在于方法1使用了一个名为“返回值优化”的概念。

方法1:

当编译器发现你没有使用它正在创建的对象而不是返回它。编译器利用了这一点,“它直接在应该返回该值的位置构建对象”。这里,只需要一个普通的构造函数调用(不需要复制构造函数),也没有析构函数调用,因为你从未真正创建过本地对象。这样效率更高。


方法2:

创建名为temp的第一个临时对象。然后复制构造函数将temp复制到oustide返回值的位置。 然后在范围结束时为析构函数调用temp。

最终方法1效率更高,但这是编译器相关的功能。

答案 3 :(得分:0)

第二个实现将导致NRV优化。 Stan Lippman说NRV优化需要一个显式的复制构造函数,但这里的类X很简单,所以我不认为NRV需要显式的复制构造函数。