Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Understanding C++ Class Default Member Functions: Copy Constructors and Operator Overloading

Tech 1

After covering destructors in a previous note, this antry continues with two essential default member functions: copy constructors and operator overloading.

Copy Constructor

A copy constructor is a special constructor whose first parameter is a reference to the same class type, and any additional parameters have default values. It is essentially a specific constructor overload.

Key characteristics of copy constructors:

  1. It is an overload of the constructor.
  2. C++ mandates that copying objects of class types must invoke the copy constructor. Hence, passing by value or returning by value for class types triggers the copy constructor.
  3. The first parameter must be a reference to the class type. Passing by value would cause infinite recursion and result in a compilation error.
  4. If not explicitly defined, the compiler generates a default copy constructor. This default performs a shallow copy (bitwise copy) for built-in type members and calls the copy constructor of each custom type member.

Consider a simple Date class. The first function below is the constructor, and the second is the copy constructor.

class Date {
public:
    Date(int year = 2024, int month = 1, int day = 1)
        : _year(year), _month(month), _day(day) {}

    Date(const Date& d)
        : _year(d._year), _month(d._month), _day(d._day) {}

    void print() const {
        std::cout << _year << "/" << _month << "/" << _day << std::endl;
    }

private:
    int _year;
    int _month;
    int _day;
};

int main() {
    Date d1(2024, 10, 5);   // Calls constructor
    Date d2 = d1;            // Calls copy constructor
    // Date d2(d1);          // Equivalent to the above line
    d1.print();
    d2.print();
    return 0;
}

When instantiating d1, we avoid Date d1(); because the compiler might interpret it as a function declaration. The object d2 is created using the copy constructor, producing a copy with the same values as d1. Both Date d2 = d1 and Date d2(d1) invoke the copy constructor.

If we omit the copy constructor for a class with only built-in members (like Date), the default copy constructor suffices due to shalow copying. However, when a class manages dynamically allocated resources, a shallow copy can cause issues.

class Stack {
public:
    Stack()
        : _a((int*)malloc(sizeof(int) * 10)), _top(0), _capacity(10) {
        if (_a == nullptr) {
            perror("malloc fail");
            return;
        }
    }

    ~Stack() {
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }

private:
    int* _a;
    int _top;
    int _capacity;
};

If we execute Stack st1; Stack st2 = st1;, the program will crash. The default shallow copy makes st2._a point to the same memory as st1._a. When both objects are destroyed, their destructors attempt to free the same memory twice, leading to undefined behavior.

Therefore, when a class explicitly implements a destructor that releases resources, a copy constructor must be written to perform a deep copy:

class Stack {
public:
    Stack()
        : _a((int*)malloc(sizeof(int) * 10)), _top(0), _capacity(10) {
        if (_a == nullptr) {
            perror("malloc fail");
            return;
        }
    }

    Stack(const Stack& s)
        : _a((int*)malloc(sizeof(int) * s._capacity)),
          _top(s._top),
          _capacity(s._capacity) {
        for (int i = 0; i < _top; ++i) {
            _a[i] = s._a[i];
        }
    }

    ~Stack() {
        free(_a);
        _a = nullptr;
        _top = _capacity = 0;
    }

private:
    int* _a;
    int _top;
    int _capacity;
};

Operator Overloading

C++ allows operators to be overloaded for user-defined types, enabling intuitive operations similar to built-in types. For instance, we can define how < works between two Date objects.

bool Date::operator<(const Date& d) const {
    if (_year < d._year)
        return true;
    else if (_year == d._year) {
        if (_month < d._month)
            return true;
        else if (_month == d._month) {
            if (_day < d._day)
                return true;
        }
    }
    return false;
}

This function is named operator< and follows the same rules as a regular function but uses the operator keyword.

Key rules for operator overloading:

  1. The number of parameters in an overloaded operator equals the number of operands. Unary operators take one parameter, binary operators take two. The left operand is past as the first argument, the right as the second.
  2. If the operator is a member function, the leftmost operand is implicitly passed via the this pointer, reducing the explicit parameter count by one.
  3. The precedence and associativity of an overloaded operator remain the same as for the built-in type.
  4. You cannot create new operator symbols by combining characters that do not exist in the syntax, such as operator@.
  5. To differentiate between prefix and postfix ++ (or --), C++ specifies that the postfix version takes an extra int parameter, allowing both to coexist as overloaded functions.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.