[12] Assignment operators  Updated! 
(Part of C++ FAQ Lite, Copyright © 1991-2010, Marshall Cline, cline@parashift.com)


FAQs in section [12]:


[12.1] What is "self assignment"?

Self assignment is when someone assigns an object to itself. For example,

 #include "Fred.h"  // Defines 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.h"  // Defines class Fred
 
 void userCode(Fred& x, Fred& y)
 {
   x = y;           
// Could be self-assignment if &x == &y
 }
 
 int main()
 {
   Fred z;
   userCode(z, z);
   
...
 }

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[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.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[12.3] OK, OK, already; I'll handle self-assignment. How do I do it?  Updated! 

[Recently rewrote to strengthen the point of not adding an unnecessary "if" (in 1/10). 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.

We will illustrate the two cases using the assignment operator in the previous FAQ:

  1. If self-assignment can be handled without any extra code, don't add any extra code. But do add a comment so others will know that your assignment operator gracefully handles self-assignment:

    Example 1a:

     Fred& Fred::operator= (const Fred& f)
     {
       
    // This gracefully handles self assignment
       *p_ = *f.p_;
       return *this;
     }

    Example 1b:

     Fred& Fred::operator= (const Fred& f)
     {
       
    // This gracefully handles self assignment
       Wilma* tmp = new Wilma(*f.p_);   
    // No corruption if this line threw an exception
       delete p_;
       p_ = tmp;
       return *this;
     }

  2. If you 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;
     }

    Or equivalently:

     Fred& Fred::operator= (const Fred& f)
     {
       if (this != &f) {   
    // Gracefully handle self assignment
         
    // Put the normal assignment duties here...
       }
       return *this;
     }

Remember: the goal is not to make self-assignment fast. Even if your object had a lot of data members, don't put an "if" test out in front just to quickly handle the self-assignment case. The self-assignment case is almost always rare, so it does not need to be efficient; it merely needs to be benign.

In other words, if you don't actually need to add the extra if statement, don't. Adding an unnecessary if statement makes a rare case faster by adding an extra conditional-branch to the normal case. If you're worried about CPU-level performance, don't punish the many to benefit the few.

TopBottomPrevious sectionNext sectionSearch the FAQ ]


[12.4] I'm creating a derived class; should my assignment operator call my base class's assignment operator?  New! 

[Recently created inspired by a question from Peter Spiekermann (in 1/10). Click here to go to the next FAQ in the "chain" of recent changes.]

Yes (if you need to define an assignment operator in the first place).

If you define your own assignment operator, the compiler will not automatically call your base class's assignment operator for you. Unless your base class's assignment operator itself is broken, you should call it explicitly from your derived class's assignment operator (again, assuming you create one in the first place).

However if you do not create your own assignment operator, the one that the compiler creates for you will automatically call your base class's assignment operator.

Example:

 class Base {
   
...
 };
 
 class Derived : public Base {
 public:
   
...
   Derived& operator= (const Derived& d);
   
...
 };
 
 Derived& Derived::operator= (const Derived& d)
 {
   
 make sure self-assignment is benign
   Base::operator= (d);
   
 do the rest of your assignment operator here...
   ...do the rest of your assignment operator here...
   return *this;
 }

TopBottomPrevious sectionNext sectionSearch the FAQ ]


E-Mail E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Mar 10, 2010