This post is part of a series of tutorials on C++

There are two additional concepts to expand on from my previous post on assignment operators.

The first is a nasty edge case. What happens (or rather, should happen) if one assigns a variable to itself? Let’s consider the template we previously introduced:

Foo& Foo::operator=(const Foo& rhs) {
  //copy from rhs to the current data
  return *this;
}

Taken from our example, we had:

ThreeLetterWord& ThreeLetterWord::operator=(const ThreeLetterWord& rhs) {
  letters[0] = rhs.letters[0];
  letters[1] = rhs.letters[1];
  letters[2] = rhs.letters[2];
  return *this;
}

Now, if rhs and *this happen to be the same member, this code’s execution will be innocuous. Each of assignments for the three letters will simply do redundant work. But, in practice, assignment can do some significantly more complicated things with memory. Some of these can just be expensive and we want to avoid them. Others might clear out memory before copying. It’s not unusual to see code like this:

Foo& Foo::operator=(const Foo& rhs) {
  delete this->some_pointer
  this->some_pointer = new ...
  //copy from rhs to this->some_pointer
  return *this;
}

Except, if rhs is the same thing as *this, the above would inadvertently delete rhs too (through the non-const access provided by this). The copy from rhs would fail entirely and likely cause some terrible seg fault.

Therefore, your assignment operator should always first check for self-assignment. It’s slightly more expensive, but often will save you huge amounts of time debugging a nasty situation you didn’t expect. And, it’s easy to do:

Foo& Foo::operator=(const Foo& rhs) {
  if (this != &rhs) {   //check for self-assignment
    //copy from rhs to the current data
  }
  return *this;
}

A second concept worth being away of is a fancy way to think about what an assignment operator should be doing in the first place. This is called the copy-and-swap assignment, which if coded correctly, provides lots of nice guarantees and can be efficient at reusing memory. It’s form looks like this:

X& X::operator=(X rhs)
{
  swap(rhs);
  return *this;
}

Note that the parameter rhs is no longer passed by const reference, but that’s usually OK. For more information, see: c++ - What is the copy-swap idiom (stackoverflow.com).

If you’re going to work with more complex types that are managing memory themselves, you’ll also want to understand c++ - What are move semantics (stackoverflow.com).



Back to C++ Tutorials