FAQs in section [12]:
[12.1] What is "self assignment"?
Self assignment is when someone assigns an object to itself. For example,
#include "Fred.hpp" // Declares class Fred
void userCode(Fred& x)
{
x = x; // Self-assignment
}
Obviously no one ever explicitly does a self assignment like the above, but
since more than one pointer or reference can point to the same object
(aliasing), it is possible to have self assignment without knowing it:
#include "Fred.hpp" // Declares class Fred
void userCode(Fred& x, Fred& y)
{
x = y; // Could be self-assignment if &x == &y
}
int main()
{
Fred z;
userCode(z, z);
}
[ Top | Bottom | Previous section | Next section ]
[12.2] Why should I worry about "self assignment"?
If you don't worry about self assignment,
you'll expose your users to some very subtle bugs that have very subtle and
often disastrous symptoms. For example, the following class will cause a
complete disaster in the case of self-assignment:
class Wilma { };
class Fred {
public:
Fred() : p_(new Wilma()) { }
Fred(const Fred& f) : p_(new Wilma(*f.p_)) { }
~Fred() { delete p_; }
Fred& operator= (const Fred& f)
{
// Bad code: Doesn't handle self-assignment!
delete p_; // Line #1
p_ = new Wilma(*f.p_); // Line #2
return *this;
}
private:
Wilma* p_;
};
If someone assigns a Fred object to itself, line #1 deletes both
this->p_ and f.p_ since *this and f are the
same object. But line #2 uses *f.p_, which is no longer a valid
object. This will likely cause a major disaster.
The bottom line is that you the author of class Fred are responsible
to make sure self-assignment on a Fred object is
innocuous. Do not assume that users won't ever do that to your
objects. It is your fault if your object crashes when it gets a
self-assignment.
Aside: the above Fred::operator= (const Fred&) has a second problem:
If an exception is thrown while evaluating new Wilma(*f.p_) (e.g., an out-of-memory
exception or an exception in Wilma's copy
constructor), this->p_ will be a dangling pointer it will
point to memory that is no longer valid. This can be solved by allocating the
new objects before deleting the old objects.
[ Top | Bottom | Previous section | Next section ]
[12.3] OK, OK, already; I'll handle self-assignment. How do
I do it? 
[Recently reworded the last paragraph (on 7/00). Click here to go to the next FAQ in the "chain" of recent changes.]
You should worry about self assignment every time you
create a class. This does not mean that you need to add extra
code to all your classes: as long as your objects gracefully handle self
assignment, it doesn't matter whether you had to add extra code or not.
If you do need to add extra code to your assignment operator, here's a simple
and effective technique:
Fred& Fred::operator= (const Fred& f)
{
if (this == &f) return *this; // Gracefully handle self assignment
// Put the normal assignment duties here...
return *this;
}
This explicit test isn't always necessary. For example, if you were to fix
the assignment operator in the previous FAQ to
handle exceptions thrown by new
and/or exceptions thrown by the copy constructor of
class Wilma, you might produce the following code. Note that this code has
the (pleasant) side effect of automatically handling self assignment as well:
Fred& Fred::operator= (const Fred& f)
{
// This code gracefully (albeit implicitly) handles self assignment
Wilma* tmp = new Wilma(*f.p_); // It would be OK if an exception got thrown here
delete p_;
p_ = tmp;
return *this;
}
In cases like the previous example (where self assignment is harmless but
inefficient), some programmers want to improve the efficiency of self
assignment by adding an otherwise unnecessary test, such as "if (this == &f) return *this;". It is generally the wrong tradeoff to make self
assignment more efficient by making the non-self assignment case less
efficient. For example, adding the above if test to the Fred assignment
operator would make the non-self assignment case slightly less efficient (an
extra (and unnecessary) conditional branch). If self assignment actually
occured once in a thousand times, the if would waste cycles 99.9% of the
time.
[ Top | Bottom | Previous section | Next section ]
E-mail the author
[ C++ FAQ Lite
| Table of contents
| Subject index
| About the author
| ©
| Download your own copy ]
Revised Jul 10, 2000
|