Main Content

Self assignment not tested in operator

Copy assignment operator does not test for self-assignment

Description

This defect occurs when you do not test if the argument to the copy assignment operator of an object is the object itself. Polyspace® does not raise this defect when you implement the copy operator by using the copy and swap idiom.

Risk

Self-assignment causes unnecessary copying. Though it is unlikely that you assign an object to itself, because of aliasing, you or users of your class cannot always detect a self-assignment.

Self-assignment can cause subtle errors if a data member is a pointer and you allocate memory dynamically to the pointer. In your copy assignment operator, you typically perform these steps:

  1. Deallocate the memory originally associated with the pointer.

    delete ptr;
    
  2. Allocate new memory to the pointer. Initialize the new memory location with contents obtained from the operator argument.

     ptr = new ptrType(*(opArgument.ptr));
    

If the argument to the operator, opArgument, is the object itself, after your first step, the pointer data member in the operator argument, opArgument.ptr, is not associated with a memory location. *opArgument.ptr contains unpredictable values. Therefore, in the second step, you initialize the new memory location with unpredictable values.

Fix

Test for self-assignment in the copy assignment operator of your class. Only after the test, perform the assignments in the copy assignment operator.

Examples

expand all

#include <algorithm>

class MyClass1 { };
class MyClass2 {
public:
	MyClass2() : p_(new MyClass1()) { }
	MyClass2(const MyClass2& f) : p_(new MyClass1(*f.p_)) { }
	~MyClass2() {
		delete p_;
	}
	MyClass2& operator= (const MyClass2& f)
	{
		delete p_;
		p_ = new MyClass1(*f.p_);
		return *this;
	}
private:
	MyClass1* p_;
};

class MyClass3 {
public:
	MyClass3& operator=( MyClass3& other) {
		MyClass3 tmp(other);
		swap(tmp);
		return *this;
	}

	void swap(MyClass3& other) noexcept{
		std::swap(obj_, other.obj_);
	}

	
private:
	
	MyClass1* obj_;
};

In this example, the copy assignment operator in MyClass2 does not test for self-assignment. If the parameter f is the current object, after the statement delete p_, the memory allocated to pointer f.p_ is also deallocated. The statement p_ = new MyClass1(*f.p_) might initializes the memory location that p_ points to with unpredictable values. Polyspace flags the copy assignment operator.

The class MyClass3 implements the copy assignment operator by using the copy-and-swap idiom. When the parameter other is the current object, the operator creates a temporary copy of the current object by using the copy constructor. Then, the objects tmp.obj_ and this.obj_ is swapped before returning the this pointer. By creating a temporary object, this copy assignment operator avoids the unexpected behavior in MyClass2::operator=. Polyspace does not flag this copy assignment operator.

Correction — Test for Self-Assignment

One possible correction is to test for self-assignment in the copy assignment operator.

class MyClass1 { };
class MyClass2 {
public:
    MyClass2() : p_(new MyClass1()) { }
    MyClass2(const MyClass2& f) : p_(new MyClass1(*f.p_)) { }
    ~MyClass2() {
        delete p_;
    }
    MyClass2& operator= (const MyClass2& f)
    {
        if(&f != this) {
           delete p_;
           p_ = new MyClass1(*f.p_);
        }
        return *this;
    }
private:
    MyClass1* p_;
};

Result Information

Group: Object oriented
Language: C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: MISSING_SELF_ASSIGN_TEST
Impact: Medium

Version History

Introduced in R2015b

expand all