22

我想确保我正确理解按值传递与按引用传递。特别是,我正在查看++对象的增量运算符的前缀/后缀版本。

假设我们有以下类X

class X{
private:
    int i;
public:
 X(){i=0;}
 X& operator ++ (){ ++i; return *this; } //prefix increment

 X operator ++ (int unused){ //postfix increment
  X ret(*this);
  i++;
  return ret;
 }

 operator int(){ return i; } //int cast
};

首先,我是否正确实现了前缀/后缀增量运算符?

其次,与前缀运算符相比,后缀运算符的内存效率如何?X具体来说,在使用每个版本的算子时,会创建多少个对象副本?

对按引用返回与按值返回究竟发生了什么的解释可能有助于我理解。


编辑:例如,使用以下代码...

X a;
X b=a++;

...现在 a 和 b 是别名吗?

4

3 回答 3

21

在后缀增量中调用对象本身的前缀增量更为惯用:

X operator++(int)
{
    X copy(*this);
    ++*this;         // call the prefix increment
    return copy;
}

因此,递增X对象的逻辑仅包含在前缀版本中。

于 2010-07-05T18:02:46.487 回答
17

这是一个正确的实现。通常,后缀运算符的性能会更差,因为您必须在进行增量之前创建另一个副本(这就是为什么我养成了始终使用前缀的习惯,除非我需要其他东西)。

使用按引用返回,您将返回对当前对象的左值引用。编译器通常会通过返回当前对象的地址来实现这一点。这意味着返回对象就像返回一个数字一样简单。

但是,对于按值返回,必须进行复制。这意味着在返回期间有更多信息要复制(而不仅仅是地址)以及要调用的复制构造函数。这就是你的表现受到影响的地方。

您的实施效率看起来与典型实施相当。

编辑:关于您的附录,不,它们不是别名。您已经创建了两个单独的对象。当您按值返回时(并且当您从后缀增量运算符中创建一个新对象时)这个新对象被放置在一个不同的内存位置。

但是,在以下代码中,a 和 b别名:

 int a = 0;
 int& b = ++a;

b 是引用 a 的地址。

于 2010-07-05T17:34:24.073 回答
2

您的运算符已正确实施。

在前缀运算符中,不复制 X。

在后缀运算符中,为 ret 创建一个副本,并且在从函数返回时可能会创建另一个副本,但所有编译器都会忽略此副本。

于 2010-07-05T17:35:07.817 回答