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
15class 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
10Widget& 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 when1
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
7Widget& 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.