Effective C++ Item 10,11 Have assignment operators return a reference to *this Handle assignment to self in operator =

时间:2023-03-09 16:03:36
Effective C++ Item 10,11 Have assignment operators return a reference to *this Handle assignment to self in operator =

If you want to concatenate assignment like this

int x, y, z;
x = y = z = 15;

The convention is to make the assignment operators return a reference to *this.

11 Handle assignmnet to self in operator =

You can easily assign an object to itself without knowing it

// Example 1
Foo *px, *py;
px = py; //Example 2
a[i] = a[i];

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

class Bitmap{...};

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

You will be dead if you assign an object to itself, beacause you will end up holding a pointer to a deleted object!

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

Widget& Widget::operator=(const Widget& rhs) {
if(this == &rhs) {
return *this;
} delete m_pb;
m_pb = new Bitmap(*rhs).m_ph;
return *this;
}

This is not the best way because above this operator is not exception-safe. What does that mean? Imagine what if an exception is thrown when

ph = new Bitmap(*rhs.m_ph)

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

Luckily, making operator= safe renders it self-assignmnet-sage, too. So, the best way to accomplish that is:

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

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

A more straightforward way to do so is to use exception-safe function swap

Widget& Widget::operator=(const Widget& ths) {
Widget temp(ths);
swap(temp);
return *this;
}