views:

219

answers:

8

Hello,

I searched how to implement + operator properly all over the internet and all the results i found do the following steps :

const MyClass MyClass::operator+(const MyClass &other) const
{
    MyClass result = *this;  // Make a copy of myself. Same as MyClass result(*this);
    result += other;         // Use += to add other to the copy.
    return result;           // All done!
}

I have few questions about this "process" :

  1. Isn't that stupid to implement + operator this way, it calls the assignment operator(which copies the class) in the first line and then the copy constructor in the return (which also copies the class , due to the fact that the return is by value, so it destroys the first copy and creates a new one.. which is frankly not really smart ... )

  2. When i write a=b+c, the b+c part creates a new copy of the class, then the 'a=' part copies the copy to himself. who deletes the copy that b+c created ?

  3. Is there a better way to implement + operator without coping the class twice, and also without any memory issues ?

thanks in advance

+5  A: 
  1. That's effectively not an assignment operator, but a copy constructor. An operation like addition creates a new value, after all, so it has to be created somewhere. This is more efficient than it seems, since the compiler is free to do Return Value Optimization, which means it can construct the value directly where it will next be used.

  2. The result is declared as a local variable, and hence goes away with the function call - except if RVO (see above) is used, in which case it was never actually created in the function, but in the caller.

  3. Not really; this method is much more efficient than it looks at first.

David Thornley
Hey DavidAbout answer number 2, i didn't quite understood your answer, lets say that no optimization is used, where exactly created the copy of b+c ?a local variable of which operator ?can you explain in more details where exactly the return value sits when the + operator is already finished but the = operator isn't began yet ... thanks
OopsUser
The return value of `operator+(b,c)` is a temporary. It's destroyed just like any other value returned from a function, i.e. at the end of the full expression.
MSalters
+1  A: 

There are no memory issues (provided that the assignment operator, and copy constructor are well written). Simply because all the memory for these objects is taken on the stack and managed by the compiler. Furthermore, compilers do optimize this and perform all the operations directly on the final a instead of copying twice.

Didier Trosset
+1  A: 
  1. This is the proper way of implementing the operator+ in C++. Most of the copies you are so afraid of will get elided by the compiler and will be subject to move semantics in C++0x.

  2. The class is a temporary and will be deleted. If you bind the temporary to a const& the life time of the temporary will be extended to the life time of the const reference.

  3. May implementing it as a freefunction is a little more obvious. The first parameter in MyClass::operator+ is an implicit this and the compiler will rewrite the function to operator+(const MyClass&, const MyClass&) anyway.

pmr
+3  A: 

This appears to be the correct way to implement operator+. A few points:

  • MyClass result = *this does not use the assignment operator, it should be calling the copy constructor, as if it were written MyClass result(*this).
  • The returned value when used in a = b + c is called a temporary, and the compiler is responsible for deleting it (which will probably happen at the end of the statement ie. the semicolon, after everything else has been done). You don't have to worry about that, the compiler will always clean up temporaries.
  • There's no better way, you need the copy. The compiler, however, is allowed to optimise away the temporary copies, so not as many as you think may be made. In C++0x though, you can use move constructors to improve performance by transfering ownership of the content of a temporary rather than copying it in its entirity.
AshleysBrain
+3  A: 

it calls the assignment operator(which copies the class) in the first line

No, this is copy-initialization (through constructor).

then the copy constructor in the return (which also copies the class

Compilers can (and typically do) elide this copy using NRVO.

When i write a=b+c, the b+c part creates a new copy of the class, then the 'a=' part copies the copy to himself. who deletes the copy that b+c created

The compiler, as any other temporary value. They are deleted at the end of full-expression (in this case, it means at or after ; at the end of line.

Is there a better way to implement + operator without coping the class twice, and also without any memory issues ?

Not really. It's not that inefficient.

jpalecek
+6  A: 

Under the circumstances, I'd probably consider something like:

MyClass MyClass::operator+(MyClass other) { 
     other += *this;
     return other;
}

Dave Abrahams wrote an article a while back explaining how this works and why this kind of code is usually quite efficient even though it initially seems like it shouldn't be.

Edit (thank you MSalters): Yes, this does assume/depend upon the commutative property holding for MyClass. If a+b != b+a, then the original code is what you want (most of the same reasoning applies).

Jerry Coffin
Note that this gives surprising results for strings. `MyString("a")+MyString("b")==MyString("ba")` ! It only works when a+b==b+a
MSalters
Thanks for the article link; the follow-on articles in the series have a really clear description of rvalue references and move semantics.
James McNellis
Just use a free function instead of a member (passing the first argument by value and the second by const ref), that omits the commutativity problem, improves encapsulation of the class and has the same usage.
Konrad Rudolph
@Konrad:quite true. I wrote it as a member function only because it was originally, but you're right: I probably shouldn't have, and a free function really is a better idea than the original member function.
Jerry Coffin
+2  A: 

I'll try my best to answer:

Point (1): No, it does not call the assignment operator. Instead it calls a constructor. Since you need to construct the object anyway (since operator+ returns a copy), this does not introduce extra operations.

Point (2): The temporary result is created in stack and hence does not introduce memory problem (it is destroyed when function exits). On return, a temporary is created so that an assignment (or copy constructor) can be used to assign the results to a (in a=b+c;) even after result is destroyed. This temporary is destroyed automatically by the compiler.

Point (3): The above is what the standard prescribes. Remember that compiler implementors are allowed to optimize the implementation as long as the effect is the same as what the standard prescribed. I believe, compilers in reality optimize away many of the copying that occurs here. Using the idiom above is readable and is not actually inefficient.

P.S. I sometime prefer to implement operator+ as a non-member to leverage implicit conversion for both sides of the operators (only if it makes sense).

Chris Henry
+1  A: 

As far as I remember, Stroustrup's 'The C++ Programming Language' recommends to implement operators as member functions only when internal representation is affected by operation and as external functions when not. operator+ does not need to access internal representation if implemented based on operator+=, which does.

So you would have:

class MyClass
{
public:
  MyClass& operator+=(const MyClass &other)
  {
    // Implementation
    return *this;
  }
};

MyClass operator+(const MyClass &op1, const MyClass &op2)
{
    MyClass r = op1;
    return r += op2;
}
Antonio Perez