Advanced 13 min

Assignment Operator

Master the assignment operator to control object assignment and handle self-assignment correctly

Learn how to implement the assignment operator to control what happens when one object is assigned to another already-existing object.

A Simple Example

#include <iostream>
#include <cstring>

class String {
private:
    char* data;
    int length;

public:
    String(const char* str = "") {
        length = std::strlen(str);
        data = new char[length + 1];
        std::strcpy(data, str);
    }

    String(const String& other) {
        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);
    }

    String& operator=(const String& other) {
        if (this == &other) {
            return *this;
        }

        delete[] data;

        length = other.length;
        data = new char[length + 1];
        std::strcpy(data, other.data);

        std::cout << "Assignment: " << data << "\n";
        return *this;
    }

    ~String() {
        delete[] data;
    }

    void display() const {
        std::cout << data << "\n";
    }
};

int main() {
    String s1{"Hello"};
    String s2{"World"};

    std::cout << "Before assignment:" << "\n";
    s1.display();
    s2.display();

    s1 = s2;

    std::cout << "After s1 = s2:" << "\n";
    s1.display();
    s2.display();

    s1 = s1;
    std::cout << "After self-assignment s1 = s1:" << "\n";
    s1.display();

    return 0;
}

Breaking It Down

Assignment vs Initialization

  • Initialization: Creating a new object - String s1{"Hello"}; or String s2 = s1;
  • Assignment: Overwriting an existing object - s1 = s2; where s1 already exists
  • Assignment must clean up old resources before copying new ones
  • Remember: Assignment operator only called on existing objects

Self-Assignment Check

  • What it does: if (this == &other) return *this; prevents assigning to yourself
  • Why it matters: Without it, obj = obj deletes data then tries to copy from deleted memory
  • How it works: Compares addresses - if same object, return immediately
  • Remember: Always check for self-assignment in assignment operators

Clean Up Before Copy

  • Step 1: Check self-assignment
  • Step 2: Delete old resources - delete[] data;
  • Step 3: Allocate new resources and copy
  • Step 4: Return *this to enable chaining
  • Remember: Forgetting to delete causes memory leaks

Return Type: ClassName&

  • Must return reference to *this for chaining: a = b = c;
  • Return type: String& operator=(const String& other)
  • Why reference: Avoids unnecessary copies, enables chaining
  • Remember: Return *this, not this - dereference the pointer

Why This Matters

  • Assignment (`obj1 = obj2`) is different from initialization. It assigns to an already-existing object, which means you must clean up the old resources first.
  • Without a proper assignment operator, you get memory leaks (old resources not freed) and crashes (shallow copies).
  • This completes the Rule of Three - destructor, copy constructor, and assignment operator work together for correct resource management.
  • Every production C++ class managing resources needs this.

Critical Insight

The copy-and-swap idiom is a clever technique that makes assignment operators exception-safe and automatically handles self-assignment:

String& operator=(String other) {  // Pass by value!
    std::swap(data, other.data);   // Swap resources
    std::swap(length, other.length);
    return *this;  // other's destructor cleans up old data
}

By passing the parameter by value instead of const reference, the copy constructor handles copying. Then we swap resources with the temporary. When the temporary dies, it cleans up our old data. Elegant and exception-safe!

Best Practices

Always check for self-assignment: Use if (this == &other) return *this; to prevent bugs.

Delete old resources before allocating new: This prevents memory leaks.

Return *this by reference: Enables assignment chaining like a = b = c;.

Consider copy-and-swap idiom: For exception safety and cleaner code in complex classes.

Common Mistakes

Not checking self-assignment: obj = obj crashes without the check.

Memory leak: Forgetting to delete old data before assigning new.

Not returning *this: Assignment should return reference for chaining.

Exception safety: If allocation fails mid-assignment, object is left in invalid state.

Debug Challenge

This assignment operator has a critical bug. Click the highlighted line to fix it:

1 String& operator=(const String& other) {
2 delete[] data;
3
4 length = other.length;
5 data = new char[length + 1];
6 std::strcpy(data, other.data);
7
8 return *this;
9 }

Quick Quiz

  1. Why check for self-assignment?
To prevent deleting data we're about to copy
For performance
It's optional
  1. What should assignment operator return?
Reference to the assigned object (`*this`)
void
A new object
  1. What's the correct order of operations in assignment?
Check self-assignment, delete old, copy new, return *this
Copy first, delete old
Doesn't matter

Practice Playground

Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.

Lesson Progress

  • Fix This Code
  • Quick Quiz
  • Practice Playground - run once