Prefix/Postfix increment operators
Asked Answered
T

3

23

I'm wanting to make sure I understand pass-by-value vs pass-by-reference properly. In particular, I'm looking at the prefix/postfix versions of the increment ++ operator for an object.

Let's suppose we have the following class 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
};

First of all, have I implemented the prefix/postfix increment operators properly?

Second, how memory-efficient is the postfix operator, compared to the prefix operator? Specifically how many X object copies are created when each version of the operator is used?

An explanation of exactly what happens with return-by-reference vs return-by-value might help me understand.


Edit: For example, with the following code...

X a;
X b=a++;

...are a and b now aliases?

Tadpole answered 5/7, 2010 at 17:27 Comment(1)
There is no need to postfix-increment i in the postfix operator. In fact, I'd do as FredOverflow suggests and call the prefix version. IMO that's indeed more idiomatic than re-implementing increment (even though the implementation is trivial here). And get rid of that implicit conversion operator. It is going to hurt you otherwise. (The third and last time I wrote an implicit conversion operator was in 2001 and one or two years later I discovered that it caused subtle bugs and removed it - like all the ones before. BTDTGTLS.)Otes
D
17

This is a correct implementation. It is typical that a postfix operator will be worse on performance because you have to create another copy before doing the increment (and this is why I've gotten in the habit of always using prefix unless I need something else).

With return-by-reference, you're returning an l-value reference to the current object. The compiler would typically implement this by returning the address of the current object. This means that returning the object is as simple as returning a number.

However, with return-by-value, a copy must be done. This means there's more information to copy over during the return (instead of just an address) as well as a copy constructor to call. This is where your performance hit comes in.

The efficiency of your implementation looks on-par with typical implementations.

EDIT: With regards to your addendum, no, they are not aliases. You have created two separate objects. When you return by value (and when you created a new object from within the postfix increment operator) this new object is placed in a distinct memory location.

However, in the following code, a and b are aliases:

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

b is an address which references a.

Digital answered 5/7, 2010 at 17:34 Comment(1)
Correct in general, modulo possible Return Value Optimization (en.wikipedia.org/wiki/Return_value_optimization).Yarmouth
J
24

It is more idiomatic to call the prefix increment of the object itself in the postfix increment:

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

The logic of incrementing an X object is thus solely contained inside the prefix version.

Juba answered 5/7, 2010 at 18:2 Comment(1)
Yep, this frees me from posting the same correction. +1 from me.Otes
D
17

This is a correct implementation. It is typical that a postfix operator will be worse on performance because you have to create another copy before doing the increment (and this is why I've gotten in the habit of always using prefix unless I need something else).

With return-by-reference, you're returning an l-value reference to the current object. The compiler would typically implement this by returning the address of the current object. This means that returning the object is as simple as returning a number.

However, with return-by-value, a copy must be done. This means there's more information to copy over during the return (instead of just an address) as well as a copy constructor to call. This is where your performance hit comes in.

The efficiency of your implementation looks on-par with typical implementations.

EDIT: With regards to your addendum, no, they are not aliases. You have created two separate objects. When you return by value (and when you created a new object from within the postfix increment operator) this new object is placed in a distinct memory location.

However, in the following code, a and b are aliases:

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

b is an address which references a.

Digital answered 5/7, 2010 at 17:34 Comment(1)
Correct in general, modulo possible Return Value Optimization (en.wikipedia.org/wiki/Return_value_optimization).Yarmouth
C
3

Your operators are implemented correctly.

In the prefix operator, no copies of X are made.

In the postfix operator, one copy is made for ret, and potentially another copy is made when returning from the function, but all compilers will elide this copy.

Collier answered 5/7, 2010 at 17:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.