C & C++

C & C++

Made by DeepSource

Unhandled self-assignment from user-defined copy constructor CXX-E2003

Bug risk
Minor

User-defined copy assignment operators that do not protect against self-assignment can result in undefined behavior and memory leaks.

This check detects such copy assignment operators and suggests two common patterns to handle self-assignment: the self-assignment check and the copy-and-swap method.

  • The self-assignment check compares the addresses of the current object and the source object to avoid unnecessary assignment.
  • The copy-and-swap method creates a temporary copy of the source object and swaps the contents with the current object.
  • An additional pattern, the copy-and-move method, involves creating a temporary copy and moving its contents into the current object.

Bad Practice

class T {
    int* p;

public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    T& operator=(const T &rhs) {
        delete p;
        p = new int(*rhs.p); // possibly a self swap, check for rhs == this
        return *this;
    }
};

Recommended

class T {
    int* p;

public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    T& operator=(const T &rhs) {
        if(this == &rhs)
            return *this;

        delete p;
        p = new int(*rhs.p);
        return *this;
    }
};
class T {
    int* p;

public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    void swap(T &rhs) {
        using std::swap;
        swap(p, rhs.p);
    }

    T& operator=(const T &rhs) {
        T(rhs).swap(*this);
        return *this;
    }
};
class T {
    int* p;

public:
    T(const T &rhs) : p(rhs.p ? new int(*rhs.p) : nullptr) {}
    ~T() { delete p; }

    // ...

    T& operator=(const T &rhs) {
        T t = rhs;
        *this = std::move(t);
        return *this;
    }

    T& operator=(T &&rhs) {
        p = rhs.p;
        rhs.p = nullptr;
        return *this;
    }
};

References