前缀/后缀增量运算符
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 创建一个副本,可能在从函数返回时创建另一个副本,但所有编译器都会忽略此副本。
【讨论】:
以上是关于前缀/后缀增量运算符的主要内容,如果未能解决你的问题,请参考以下文章