Search

9.14 — Overloading the assignment operator

The assignment operator (operator=) is used to copy values from one object to another already existing object.

Assignment vs Copy constructor

The purpose of the copy constructor and the assignment operator are almost equivalent -- both copy one object to another. However, the copy constructor initializes new objects, whereas the assignment operator replaces the contents of existing objects.

The difference between the copy constructor and the assignment operator causes a lot of confusion for new programmers, but it’s really not all that difficult. Summarizing:

  • If a new object has to be created before the copying can occur, the copy constructor is used (note: this includes passing or returning objects by value).
  • If a new object does not have to be created before the copying can occur, the assignment operator is used.

Overloading the assignment operator

Overloading the assignment operator (operator=) is fairly straightforward, with one specific caveat that we’ll get to. The assignment operator must be overloaded as a member function.

This prints:

5/3

This should all be pretty straightforward by now. Our overloaded operator= returns *this, so that we can chain multiple assignments together:

Issues due to self-assignment

Here’s where things start to get a little more interesting. C++ allows self-assignment:

This will call f1.operator=(f1), and under the simplistic implementation above, all of the members will be assigned to themselves. In this particular example, the self-assignment causes each member to be assigned to itself, which has no overall impact, other than wasting time. In most cases, a self-assignment doesn’t need to do anything at all!

However, in cases where an assignment operator needs to dynamically assign memory, self-assignment can actually be dangerous:

First, run the program as it is. You’ll see that the program prints “Alex” as it should.

Now run the following program:

You’ll probably get garbage output. What happened?

Consider what happens in the overloaded operator= when the implicit object AND the passed in parameter (str) are both variable alex. In this case, m_data is the same as str.m_data. The first thing that happens is that the function checks to see if the implicit object already has a string. If so, it needs to delete it, so we don’t end up with a memory leak. In this case, m_data is allocated, so the function deletes m_data. But str.m_data is pointing to the same address! This means that str.m_data is now a dangling pointer.

Later on, we allocate new memory to m_data (and str.m_data). So when we subsequently copy the data from str.m_data into m_data, we’re copying garbage, because str.m_data was never initialized.

Detecting and handling self-assignment

Fortunately, we can detect when self-assignment occurs. Here’s an updated implementation of our overloaded operator= for the MyString class:

By checking if the address of our implicit object is the same as the address of the object being passed in as a parameter, we can have our assignment operator just return immediately without doing any other work.

Because this is just a pointer comparison, it should be fast, and does not require operator== to be overloaded.

When not to handle self-assignment

First, there is no need to check for self-assignment in a copy-constructor. This is because the copy constructor is only called when new objects are being constructed, and there is no way to assign a newly created object to itself in a way that calls to copy constructor.

Second, the self-assignment check may be omitted in classes that can naturally handle self-assignment. Consider this Fraction class assignment operator that has a self-assignment guard:

If the self-assignment guard did not exist, this function would still operate correctly during a self-assignment (because all of the operations done by the function can handle self-assignment properly).

Because self-assignment is a rare event, some prominent C++ gurus recommend omitting the self-assignment guard even in classes that would benefit from it. We do not recommend this, as we believe it’s a better practice to code defensively and then selectively optimize later.

Default assignment operator

Unlike other operators, the compiler will provide a default public assignment operator for your class if you do not provide one. This assignment operator does memberwise assignment (which is essentially the same as the memberwise initialization that default copy constructors do).

Just like other constructors and operators, you can prevent assignments from being made by making your assignment operator private or using the delete keyword:


9.15 -- Shallow vs. deep copying
Index
9.13 -- Converting constructors, explicit, and delete

108 comments to 9.14 — Overloading the assignment operator

  • Sid22

    In the book I am reading, it is written about overloaded assignment operator with its return type as constant reference :

    (where Array is a user defined class implementing dynamic array)
    It says:
    " Regardless of whether this is a self-assignment, the member function (talking about the assignment operator overloading function) returns the current object (i.e., *this) as a constant reference; this enables cascaded Array assignments such as x = y = z, but prevents ones like (x = y) = z because z cannot be assigned to the const Array reference that’s returned by (x = y). "

    -> Why is (x=y)=z any different than x=y=z?
    ->What is the difference between

    and

    • nascardriver

      The assignment operator is evaluated right-to-left, ie.

      Here, it doesn't matter whether `(d = e)` is `const` or not, because it doesn't get modified. In
      `(x = y) = z`, we're trying to modify a `const` reference, which doesn't work.

      I can't tell which version is better.

  • David

    I have a question in the following code.As you mentoned,"If a new object has to be created before the copying can occur, the copy constructor is used",so why is the copy constructor not used ? We have to create a new object named f before we copy the contents of the object named fiveThird into the object named f.

    I think this behavior meets your description for the copy constructor.Does my concept be wrong?

    • sito

      alright. So I think i have an answer. I don't know if this will answer your question so Alex or Nascardriver feel free to corret me if i'm wrong.
      The assignement operator copys and replases the content of the object with new values that are being assigned. In order for this to work though the object have to exist otherwise you can't replase anything since there is not an existing object to plase the copied content from the copied object in. So if you do something like this, Fraction f3=f1 the compiler will check if f3 already exists. The compiler will then determine that f3 have not been created before so assigning a value to it won't work. The compiler will instead create f3 and use the copy constructor.
      You can test this to see what I mean.
      Sorry if this is more rambling then a good answer but the best way to understand the diference is to test your code.

  • Victor

    Inside the assignment operator of MyString, do I really need to delete[] m_data? Reusing the allocated memory would lead to problems? If instead of deleting and allocating I do something like:

    Would it be wrong?

Leave a Comment

Put all code inside code tags: [code]your code here[/code]