In Herb Sutter's book Exceptional C++ (1999), he has words in item 10's solution:
"Exception-unsafe" and "poor design" go hand in hand. If a piece of code isn't exception-safe, that's generally okay and can simply be fixed. But if a piece of code cannot be made exception-safe because of its underlying design, that almost always is a signal of its poor design.
Example 1: A function with two different responsibilities is difficult to make exception-safe.
Example 2: A copy assignment operator that is written in such a way that it must check for self-assignment is probably not strongly exception-safe either
What does he mean by the term "check for self-assignment"?
[INQUIRY]
Dave and AndreyT shows us exactly what "check for self-assignment" means. That's good. But the question is not over. Why does "check for self-assignment" hurts "exception safety"(according to Hurb Sutter)? If the caller tries to do self-assignment, that "check" works as if no assignment ever occurs. Does it really hurt?
[MEMO 1] In item 38 Object Identity later in Herb's book, he explains about self-assignment.
To answer my rhetorical question: It means that a well-designed assignment operator should not need to check for self-assignment. Assigning an object to itself should work correctly (i.e. have the end-effect of "doing nothing") without performing an explicit check for self-assignment.
For example, if I wanted to implement a simplistic array class along the lines of
class array {
// code...
int *data;
size_t n;
};
...and came up with the following implementation of the assignment operator...
array &array::operator =(const array &rhs)
{
delete[] data;
n = rhs.n;
data = new int[n];
std::copy_n(rhs.data, n, data);
return *this;
}
In order to "fix" this, you have two options;
array &array::operator =(const array &rhs) {
if (&rhs != this) {
delete[] data;
n = rhs.n;
data = new int[n];
std::copy_n(rhs.data, n, data);
}
return *this;
}
array &array::operator =(const array &rhs) {
size_t new_n = rhs.n;
int *new_data = new int[new_n];
std::copy_n(rhs.data, new_n, new_data);
delete[] data;
n = new_n;
data = new_data;
return *this;
}
The latter approach is better in a sense that it works correctly in self-assignment situations without making an explicit check. This implementation is far for perfect from a 'safety point of view', it is here to illustrate the difference between "checked" and "check-less" approaches to handling self-assignment. The later check-less implementation can be written more elegantly through the well-known copy-and-swap idiom.
This does not mean that you should avoid explicit checks for self-assignment. Such check do make sense from the performance point of view: there's no point in carrying out a long sequence of operations just to end up "doing nothing" in the end. But in a well-designed assignment operator such checks should not be necessary from the correctness point of view.