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


FAQs in section [12]:


[12.1] What is "self assignment"?

Self assignment is when someone assigns an object with 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 knowning it:

    #include "Fred.hpp"    // Declares class Fred
    
    void userCode(Fred& x, Fred& y)
    {
      x = y;   
// Could be self-assignment if &x == &y
    }
    
    main()
    {
      Fred z;
      userCode(z, z);
    }

TopBottomPrevious sectionNext 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 with 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 inocuous. 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 section ]


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

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& 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;
    }

Some programmers want to add "if (this == &f) return *this;" to make self assignment more efficient. This is generally the wrong tradeoff. If self assignment only occurs once in a thousand times, the if would waste cycles in 99.9% of the time (a test-and-branch can put a bubble in the pipeline of many superscalar processors).

TopBottomPrevious sectionNext section ]


 E-mail the author
C++ FAQ LiteTable of contentsSubject indexAbout the author©Download your own copy ]
Revised Sep 8, 1997