5

I read about this from "Effective c++" ,this is Col.10. It say it's a good way to have assignment operators return a reference to *this. I wrote a code snippet to test this idea. I overridden the assignment operator here.And tested it. Everything is fine. But when I remove that operator overriding, everything is the same. That means, the chaining assignment still works well. So, what am I missing? Why is that? Need some explanation from you guys, THank you.

#include <iostream>

using namespace std;

class Widget{
public:

    Widget& operator=(int rhs)
    {
        return *this;
    }
    int value;

};

int main()
{
    Widget mywidget;
    mywidget.value = 1;
    Widget mywidget2;
    mywidget2.value = 2;
    Widget mywidget3 ;
    mywidget3.value = 3;
    mywidget = mywidget2 = mywidget3;
    cout << mywidget.value<<endl;
    cout << mywidget2.value<<endl;
    cout << mywidget3.value<<endl;

}
Don Lun
  • 2,717
  • 6
  • 29
  • 35

3 Answers3

7

If you remove completely the operator= method, a default operator= will be created by the compiler, which implements shallow copy1 and returns a reference to *this.

Incidentally, when you write

mywidget = mywidget2 = mywidget3;

you're actually calling this default operator=, since your overloaded operator is designed to work with ints on the right side.

The chained assignment will stop working, instead, if you return, for example, a value, a const reference (=>you'll get compilation errors) or a reference to something different from *this (counterintuitive stuff will start to happen).

Partially related: the copy and swap idiom, i.e. the perfect way to write an assignment operator. Strongly advised read if you need to write an operator=


  1. The default operator= will perform as if there were an assignment between each member of the left hand operand and each member of the right hand one. This means that for primitive types it will be a "brutal" bitwise copy, which in 90% of cases isn't ok for pointers to owned resources.
Community
  • 1
  • 1
Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
  • oh, I see. So, should I always do this in my code? Or just let compiler to deal with it? – Don Lun Apr 14 '11 at 21:20
  • 3
    You need to implement `operator=` if your class needs it, i.e. if default bitwise copy isn't ok for it. This typically happens when you have raw pointers to memory/handles to resources owned by your class; a good rule of thumb is that, if you need a destructor to free some resources, you will probably need an `operator=` to avoid unwanted sharing of these resources between different instances, double frees and resource leaks. – Matteo Italia Apr 14 '11 at 21:22
  • 2
    Not really a bitwise copy... for members that do not have their own `operator=` it will perform a bitwise copy, but for members that implement it (think on an `std::string`) the existing `operator=` for the member will be used. – David Rodríguez - dribeas Apr 14 '11 at 21:26
  • what is the case for "pointers to owned resources."? can you give me a simple example? – Don Lun Apr 14 '11 at 21:35
  • @Don Lun: say your class allocates a dynamic array at construction, stores it in a pointer member and deallocates it in the destructor; everything seems to work fine, but if you assign an instance of such class to another one with the default `operator=` you'll get (1) a memory leak, since the target instance's array pointer will be overwritten without any chance to free the memory pointed (2) an unwanted resource sharing, because now both instances are using the same internal array, and (3) a double free, because both instances will try to `delete[]` the same array on destruction. – Matteo Italia Apr 14 '11 at 21:43
5

The question touches two different concepts, whether you should define operator= and whether in doing so you should return a reference to the object.

You should consider the rule of the three: if you define one of copy constructor, assignment operator or destructor you should define the three of them. The rationale around that rule is that if you need to provide a destructor it means that you are managing a resource, and in doing so, chances are that the default copy constructor and assignment operator won't cut it. As an example, if you hold memory through a raw pointer, then you need to release the memory in the destructor. If you don't provide the copy constructor and assignment operator, then the pointer will be copied and two different objects will try to release the memory held by the pointer.

While a pointer is the most common example, this applies to any resource. The exception is classes where you disable copy construction and assignment --but then again you are somehow defining them to be disabled.

On the second part of the question, or whether you should return a reference to the object, you should. The reason, as with all other operator overloads is that it is usually a good advice to mimic what the existing operators for basic types do. This is sometimes given by a quote: when overloading operators, do as ints do.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
1

Widget& operator=(int rhs)

This allows you to assign an int to a Widget - e.g. mywidget = 3;

Make a Widget& operator=(Widget const & rhs) - it'll be called for your mywidget = mywidget2 = mywidget3; line.

You do not need an operator=(Widget const & rhs) though - the default should do fine.

Also, it may be a good idea to add e.g. cout << "operator=(int rhs)\n"; to your custom operator - then you'd see that it didn't get called at all in your code.

Erik
  • 88,732
  • 13
  • 198
  • 189