C++ Class Default Assignment Operator

5 min read Jul 01, 2024
C++ Class Default Assignment Operator

C++ Class Default Assignment Operator

The default assignment operator in C++ is a member function that is automatically generated by the compiler if you don't explicitly define it. It performs a shallow copy of the object, meaning it copies the values of the members of the object. While this might seem like a convenient feature, it can lead to unexpected behavior and potential memory leaks, especially when dealing with objects that contain pointers or dynamic memory.

Understanding Shallow Copy

When the default assignment operator performs a shallow copy, it simply copies the values of the members of the object. If an object contains a pointer, the default assignment operator will copy the value of the pointer, which is the memory address it points to.

Consider this example:

#include 

class MyClass {
public:
    int *ptr;

    MyClass() {
        ptr = new int(5);
    }
};

int main() {
    MyClass obj1;
    MyClass obj2;

    // Default assignment operator performs a shallow copy
    obj2 = obj1;

    // Modify the value pointed to by obj1's pointer
    *(obj1.ptr) = 10;

    // Print the values pointed to by both objects
    std::cout << "obj1.ptr: " << *(obj1.ptr) << std::endl; 
    std::cout << "obj2.ptr: " << *(obj2.ptr) << std::endl; 

    return 0;
}

In this example, both obj1 and obj2 will point to the same memory location. Modifying the value pointed to by obj1.ptr will also change the value pointed to by obj2.ptr. This can lead to unexpected results and potential memory leaks if the object is deleted before the copy.

Why You Should Define Your Own Assignment Operator

Defining your own assignment operator allows you to implement a deep copy mechanism, where you create a new copy of the object's data, including any dynamically allocated memory. This ensures that your objects remain independent and avoid unexpected behavior.

Here's how you can define a custom assignment operator:

#include 

class MyClass {
public:
    int *ptr;

    MyClass() {
        ptr = new int(5);
    }

    MyClass(const MyClass& other) {
        ptr = new int(*other.ptr); 
    }

    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            delete ptr; // Deallocate existing memory
            ptr = new int(*other.ptr); // Allocate new memory and copy value
        }
        return *this;
    }

    ~MyClass() {
        delete ptr;
    }
};

int main() {
    MyClass obj1;
    MyClass obj2;

    // Custom assignment operator performs a deep copy
    obj2 = obj1;

    // Modify the value pointed to by obj1's pointer
    *(obj1.ptr) = 10;

    // Print the values pointed to by both objects
    std::cout << "obj1.ptr: " << *(obj1.ptr) << std::endl; 
    std::cout << "obj2.ptr: " << *(obj2.ptr) << std::endl; 

    return 0;
}

In this example, the custom assignment operator handles the following:

  1. Self-assignment check: if (this != &other) prevents the object from assigning itself, which could lead to memory leaks.
  2. Deallocation: delete ptr; deallocates the memory pointed to by the existing ptr.
  3. Allocation and copy: ptr = new int(*other.ptr); allocates new memory and copies the value from the other object.

Summary

The default assignment operator in C++ performs a shallow copy, which can lead to unexpected behavior and potential memory leaks. You should always define your own assignment operator to implement a deep copy and ensure the proper handling of dynamically allocated memory. This will help you write safer and more reliable code.