You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 32 Next »

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

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.

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:

Thingy& Thingy::operator=(const Thingy& rhs) {
    Widget *original = w;
    w = new Widget(*rhs.w);
    delete original;
    return *this;
}

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:

class Thingy {
    public:
        // ...
        // compiler-generated copy assignment now correct
    private:
        std::tr1::shared_ptr<Widget> w;
};

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

MEM42-CPP

low

probable

high

P2

L3

Bibliography

[Henricson 97] Rule 5.12 Copy assignment operators should be protected from doing destructive actions if an object is assigned to itself.

[Meyers 05] Item 11: Handle assignment to self in operator=.


MEM41-CPP. Declare a copy constructor, a copy assignment operator, and a destructor in a class that manages resources      08. Memory Management (MEM)      MEM44-CPP. Do not leak resources when handling exceptions

  • No labels