Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The copy assignment operator must work in the event of self-assignment. The obvious approach to implementing the assignment operator is to delete existing pointers and then assign them to copies of the data members of the new object. This could well lead to self-assignment corrupting the state of the object: the chances are that pointers are deleted and then assigned to themselves. To prevent this, the new object state could be built in local variables before being assigned to the data members. However, this could be very inefficient. A better approach is to test for self-assignment (by testing this against the value of the argument passed to the assignment operator) and then do nothing, and even better approaches may be available.

Non-Compliant Code Example

Code Block
bgColor#FFcccc
class Widget {
    // ...
};
class Thingy {
    public:
        // ...
        Thingy& operator=(const Thingy& rhs);
        // ...
    private:
        Widget *w;
};
// ...
Thingy& Thingy::operator=(const Thingy& rhs) {
    delete w;                // delete the current Widget
    w = new Widget(*rhs.w);  // create a copy of rhs's Widget
    return *this;
}
// ...

If this copy assignment operator is used in a self-assignment, the delete operator not only deletes the Widget of the current object, it also deletes rhs's Widget, resulting in the Thingy containing a pointer to a deleted object!

Compliant Solution 1

Better is to test for self-assignment, and to carry out the work of the assignment only in the case that it is not a self-assignment.

Code Block
bgColor#ccccff
Thingy& Thingy::operator=(const Thingy& rhs) {
    if (this != &rhs) {
        delete w;
        w = new Widget(*rhs.w);
    }
    return *this;
}

Compliant Solution 2

Alternatively, a copy to the original resource can be taken and not deleted until the new value has been established:

...

This version of the code has the advantage that it is exception-safe: if the call of new throws an exception, the original Thingy (and the Widget that it contains) remains unchanged.

Compliant Solution 3

Alternatively, a resource managing smart pointer (or other resource managing container) may be used to hold the external resource:

...

This version of the code is also exception-safe, because the implementation of shared_ptr is exception-safe.

Risk Assessment

Allowing a copy assignment operator to corrupt an object could lead to undefined behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

RES36-C

1 (low)

2 (probable)

1 (high)

P2

L3

References

Wiki Markup
\[[Henricson 97|AA. C++ References#Henricson 97]\] Rule 5.12 Copy assignment operators should be protected from doing destructive actions if an object is assigned to itself.

...