06 Operator Overloading & Initialization

Overview: Initialization Semantics & Syntax Overloads

C++ distinguishes between initialization and assignment. This note covers member initialization list performance, operator overloading design rules, and this pointer mechanics.


1. Member Initialization Lists vs. Assignment

A constructor can initialize its members using an Initialization List or by assignment inside the constructor body.

// Method A: Member Initialization List (Preferred)
Point(double a, double b) : x(a), y(b) {}
 
// Method B: Assignment inside body (Avoid unless necessary)
Point(double a, double b) {
    x = a;
    y = b;
}

Why Initialization Lists are preferred:

  1. Performance (Zero Double-Initialization): Method B first calls the default constructor for x and y, then overwrites them using the assignment operator. Method A constructs them in-place with the correct values immediately.
  2. Mandatory cases: const members and reference variables (Type&) must be initialized via the initialization list because they cannot be assigned after construction.

The Declaration Order Gotcha:

Members are always initialized in the order they are declared in the class definition, regardless of their order in the initialization list.

class Foo {
    int a;
    int b;
public:
    Foo(int x) : b(x), a(b + 1) {} // WARNING: 'a' is initialized first with uninitialized 'b'!
};

2. Operator Overloading Design Rules

Operator overloading provides clean syntax for custom types.

Point operator+(const Point& rhs) const;
  • Why the const Point& rhs parameter? Prevents copying the right-hand side object (performance) while guaranteeing it won’t be modified (safety).
  • Why is the function marked const at the end? Guarantees the operation does not modify the left-hand side object (allows up + right without altering up).

3. The this Pointer & Chain Assignments

Within any non-static member function, this is a hidden pointer pointing to the active object instance.

For compound assignment operators (like +=), it is standard practice to return a reference to the modified object (*this):

Point& Point::operator+=(const Point& rhs) {
    x += rhs.x;
    y += rhs.y;
    return *this; // Returns a reference to the modified object
}

Why return a reference?

Returning Point& allows you to chain operators (e.g. (p1 += p2) += p3) and avoids the overhead of copy-constructing a temporary object during assignment.


💻 Conceptual Code Demonstration

#include <iostream>
 
class Vector2D {
public:
    double x;
    double y;
    const double limit; // Must be initialized in initializer list
 
    // 1. Initializer List constructor
    Vector2D(double xVal, double yVal, double lim) : x(xVal), y(yVal), limit(lim) {}
 
    // 2. Binary addition operator overload
    Vector2D operator+(const Vector2D& rhs) const {
        double newX = x + rhs.x;
        double newY = y + rhs.y;
        return Vector2D(newX > limit ? limit : newX, 
                        newY > limit ? limit : newY, 
                        limit);
    }
 
    // 3. Assignment operator overload returning reference
    Vector2D& operator+=(const Vector2D& rhs) {
        x += rhs.x;
        y += rhs.y;
        if (x > limit) x = limit;
        if (y > limit) y = limit;
        return *this; // Dereference pointer to return reference
    }
};
 
int main() {
    Vector2D a(10.0, 5.0, 20.0);
    Vector2D b(5.0, 10.0, 20.0);
 
    // Operates as: a.operator+(b)
    Vector2D result = a + b;
    std::cout << "Result: " << result.x << ", " << result.y << '\n';
 
    // Chained assignment operates as: (a.operator+=(b)).operator+=(b)
    a += b;
    std::cout << "a updated: " << a.x << ", " << a.y << '\n';
    
    return 0;
}