assignment_operator


Assignment Operator

The assignment operator (=) in C++ is a fundamental tool for object-oriented programming, allowing objects to be assigned values after their creation. When working with user-defined classes, it's often necessary to provide a custom implementation of the assignment operator to ensure proper resource management and deep copying of data members. This topic explores various aspects of the assignment operator in class construction.

Example 1: Basic Assignment Operator Overloading

#include <iostream>
#include <cstring>

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

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

    ~String() {
        delete[] data;
    }

    // Copy assignment operator
    String& operator=(const String& other) {
        if (this != &other) {
            delete[] data;
            length = other.length;
            data = new char[length + 1];
            strcpy(data, other.data);
        }
        return *this;
    }

    void print() const {
        std::cout << data << std::endl;
    }
};

int main() {
    String s1("Hello");
    String s2;
    s2 = s1;  // Assignment operator called

    s1.print();  // Output: Hello
    s2.print();  // Output: Hello

    return 0;
}

Explanation

This example demonstrates a basic implementation of the assignment operator for a simple String class:

Example 2: Move Assignment Operator

#include <iostream>
#include <utility>

class Resource {
private:
    int* data;
    size_t size;

public:
    Resource(size_t n = 0) : size(n), data(n ? new int[n]() : nullptr) {}

    ~Resource() {
        delete[] data;
    }

    // Copy assignment operator
    Resource& operator=(const Resource& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = new int[size];
            std::copy(other.data, other.data + size, data);
        }
        return *this;
    }

    // Move assignment operator
    Resource& operator=(Resource&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
        }
        return *this;
    }

    void print() const {
        for (size_t i = 0; i < size; ++i) {
            std::cout << data[i] << " ";
        }
        std::cout << std::endl;
    }
};

int main() {
    Resource r1(5);
    Resource r2;

    // Copy assignment
    r2 = r1;

    // Move assignment
    Resource r3;
    r3 = std::move(r1);

    r2.print();  // Output: 0 0 0 0 0
    r3.print();  // Output: 0 0 0 0 0
    r1.print();  // Output: (empty, as r1 has been moved from)

    return 0;
}

Explanation

This example showcases both copy and move assignment operators:

Example 3: Assignment Operator in a Hierarchy

#include <iostream>
#include <string>

class Animal {
protected:
    std::string name;

public:
    Animal(const std::string& n = "") : name(n) {}

    Animal& operator=(const Animal& other) {
        if (this != &other) {
            name = other.name;
        }
        return *this;
    }

    virtual void speak() const {
        std::cout << name << " makes a sound." << std::endl;
    }
};

class Dog : public Animal {
private:
    std::string breed;

public:
    Dog(const std::string& n = "", const std::string& b = "") : Animal(n), breed(b) {}

    Dog& operator=(const Dog& other) {
        if (this != &other) {
            Animal::operator=(other);  // Call base class assignment operator
            breed = other.breed;
        }
        return *this;
    }

    void speak() const override {
        std::cout << name << " the " << breed << " barks." << std::endl;
    }
};

int main() {
    Dog d1("Buddy", "Labrador");
    Dog d2;
    d2 = d1;  // Assignment operator called

    d1.speak();  // Output: Buddy the Labrador barks.
    d2.speak();  // Output: Buddy the Labrador barks.

    return 0;
}

Explanation

This example demonstrates the assignment operator in a class hierarchy:

Summary

The assignment operator is a crucial component in C++ class design, especially when dealing with resource management. Key points to remember:

  1. Always check for self-assignment to prevent unnecessary operations and potential errors.
  2. Implement deep copying for classes managing dynamic resources to avoid shallow copy issues.
  3. Consider implementing both copy and move assignment operators for efficiency, especially when dealing with large resources.
  4. In class hierarchies, derived class assignment operators should call the base class assignment operator to ensure proper copying of inherited members.
  5. Return a reference to *this to allow chaining of assignments.
  6. Mark move assignment operators as noexcept when possible for optimization and exception safety.

By properly implementing assignment operators, you ensure that your classes behave correctly and efficiently when objects are assigned new values, contributing to robust and maintainable C++ code.

Related

Previous Page | Course Schedule | Course Content