When writing a copy assignment operator, it is very important that it be able to work in the event of self-assignment. That is, it has to allow this:
SomeType t = ...;
t = t;
Self-assignment usually doesn't happen in such an obvious way. It typically happens via a circuitous route through various code systems, where the location of the assignment simply has two Person
pointers or references and has no idea that they are the same object.
Any copy assignment operator you write must be able to take this into account.
The typical way to do so is to wrap all of the assignment logic in a condition like this:
SomeType &operator=(const SomeType &other)
{
if(this != &other)
{
//Do assignment logic.
}
return *this;
}
Note: It is important to think about self-assignment and ensure that your code behaves correctly when it happens. However, self-assignment is a very rare occurrence and optimizing to prevent it may actually pessimize the normal case. Since the normal case is much more common, pessimizing for self-assignment may well reduce your code efficiency (so be careful using it).
As an example, the normal technique for implementing the assignment operator is the copy and swap idiom
. The normal implementation of this technique does not bother to test for self-assignment (even though self-assignment is expensive because a copy is made). The reason is that pessimization of the normal case has been shown to be much more costly (as it happens more often).
Move assignment operators must also be protected against self-assignment. However, the logic for many such operators is based on std::swap
, which can handle swapping from/to the same memory just fine. So if your move assignment logic is nothing more than a series of swap operations, then you do not need self-assignment protection.
If this is not the case, you must take similar measures as above.