前缀/后缀增量运算符

Posted

技术标签:

【中文标题】前缀/后缀增量运算符【英文标题】:Prefix/Postfix increment operators 【发布时间】:2011-03-12 00:09:44 【问题描述】:

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

假设我们有以下类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 现在是别名了吗?

【问题讨论】:

不需要在后缀运算符中后缀递增i。事实上,我会使用 FredOverflow suggests 并调用前缀版本。 IMO 确实比重新实现增量更惯用(即使这里的实现很简单)。 并摆脱那个隐式转换运算符。否则它会伤害你。 (我第三次也是最后一次编写隐式转换运算符是在 2001 年,一两年后我发现它会导致一些细微的错误并将其删除 - 就像之前的所有错误一样。BTTDGTLS。) 【参考方案1】:

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

X operator++(int)

    X copy(*this);
    ++*this;         // call the prefix increment
    return copy;

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

【讨论】:

是的,这让我不必发布相同的更正。 +1来自我。【参考方案2】:

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

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

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

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

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

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

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

b 是引用 a 的地址。

【讨论】:

大体上是正确的,模可能的返回值优化 (en.wikipedia.org/wiki/Return_value_optimization)。【参考方案3】:

您的操作符实现正确。

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

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

【讨论】:

以上是关于前缀/后缀增量运算符的主要内容,如果未能解决你的问题,请参考以下文章

短路评估顺序和前缀增量运算符

前缀增量运算符错误 C++

将值传递给函数时,GCC前缀增量运算符行为不端[重复]

关于 Java 8(OCA)中的短路运算符和后缀增量的问题 [关闭]

前缀中缀后缀表达式

前缀和后缀运算符继承[重复]