Effective C++ item 11: Handle assignment to self in operator=.

You can easily assign an object to itself without knowing it.

1
2
3
4
5
6
7
// Example 1
Foo * px, py;
px = py;
*px = *py;

// Example 2
a[i] = a[i] // when i = j

It is dangerous to assign an object to itself if your class doesn’t handle this kind of operator appropriately. Let’s consider an example here:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Bitmap{...};
class Widget
{
...
private:
Bitmap* m_pb;
};

// Assignment operator of class Widget
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.m_pb);
return *this;
}

You will be dead if you assign a Widget object to itself cause you will end up with holding a pointer to a deleted object!

One way, but not best way to deal with self assignment is to write code to explicitly deal with this situation:

1
2
3
4
5
6
7
8
9
10
Widget& Widget::operator=(const Widget& rhs)
{
if (this == &rhs)
{
return *this; // if a self-assignment, do nothing
}
delete pb;
pb = new Bitmap(*rhs.m_pb);
return *this;
}

This is not the best way to solve the problem cause this operator is not exception-safe. What does this mean? Imagine what if an exception is thrown when

1
pb = new Bitmap(*rhs.m_pb);

is been executed (for example, not enough memory), then the object will hold a pointer to a deleted Bitmap.

Luckily, making operator= exception-save typically renders it self-assignment-safe, too. So the best way to accomplish that is:

1
2
3
4
5
6
7
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* temp = pb;
pb = new Bitmap(*rhs.pb);
delete temp;
return *this;
}

Now if “new Bitmap” throws an exception, pb remains unchanged. Even without the identity test, this code handles assignment to self, because we make a copy of the original bitmap, point to the copy we made, and then delete the original bitmap.

Reference:
“Effective C++” Third Edition by Scott Meyers.