Operator Overloading  «Prev  Next»
Lesson 17

Overloading Operators in C++ Conclusion

In this module the basics of overloading operators were discussed.
You also have a grasp of how to build a non-native type with a consistent and complete interface. In particular, you learned:
  1. Why operator overloading is useful in C++:
    Operator overloading in C++ is useful because it allows developers to redefine the behavior of operators (like +, -, *, ==, etc.) for user-defined types, such as classes or structs, making code more intuitive, readable, and expressive. Here are the key benefits:
    • Intuitive Syntax: It enables objects to be manipulated using operators in a way that feels natural, mimicking built-in types. For example, adding two Complex number objects with c1 + c2 is clearer than calling a function like c1.add(c2).
    • Code Readability: Overloaded operators can make complex operations look simpler, improving code clarity. For instance, Matrix m3 = m1 + m2 is more concise than Matrix m3 = m1.addMatrix(m2).
    • Consistency with Built-in Types: It aligns the behavior of custom types with built-in types, allowing seamless integration. For example, overloading << for a class lets you output objects to streams like std::cout << myObject, similar to built-in types.
    • Flexibility: Developers can tailor operator behavior to suit specific needs, such as defining + for string concatenation in a custom String class or for vector addition in a Vector class.
    • Encapsulation: Operator overloading keeps related operations within the class, maintaining encapsulation while providing a clean interface.

    Example
    class Complex {
    public:
        double real, imag;
        Complex(double r, double i) : real(r), imag(i) {}
        Complex operator+(const Complex& other) const {
            return Complex(real + other.real, imag + other.imag);
        }
    };
    
    Complex c1(1, 2), c2(3, 4);
    Complex c3 = c1 + c2; // Intuitive: c3 is (4, 6)
    

    Caveats
    • Overloading must be done judiciously to avoid confusion (e.g., defining + to subtract would be misleading).
    • Not all operators can be overloaded (e.g., ::, ., sizeof).
    • Performance considerations apply, as overloaded operators are essentially function calls.

    In summary, operator overloading enhances expressiveness and usability in C++ by allowing custom types to leverage familiar operator syntax, provided it’s used logically and consistently.
  2. Which operators can be overloaded
  3. How and when to use friend functions to access class members
  4. How to overload unary operators
  5. How to overload binary operators
  6. How to overload input and output operators
  7. How to overload the member pointer operator
  8. How to overload the new and delete operators

C++ Operator Overloading Guidelines

In C++ you can give special meanings to operators, when they are used with user-defined classes and this is called operator overloading. You can implement C++ operator overloads by providing special member-functions on your classes that follow a particular naming convention. For example, to overload the + operator for your class, you would provide a member-function named operator+ on your class.
The following set of operators is commonly overloaded for user-defined classes:
  1. = (assignment operator)
  2. + - * (binary arithmetic operators)
  3. += -= *= (compound assignment operators)
  4. == != (comparison operators)
Here are some guidelines for implementing these operators. These guidelines are very important to follow.

Assignment Operator =
The assignment operator has a signature like this:
 class MyClass {
 public:
   ...
   MyClass & operator=(const MyClass & rhs);
   ...
 }

 MyClass a, b;
 ...
 b = a;   // Same as b.operator=(a);

Notice that the = operator takes a const-reference to the right hand side of the assignment. The reason for this should be obvious, since we don't want to change that value; we only want to change what is on the left hand side. Also, you will notice that a reference is returned by the assignment operator. This is to allow operator chaining. You typically see it with primitive types, like this:
int a, b, c, d, e;
a = b = c = d = e = 42;

This is interpreted by the compiler as:
a = (b = (c = (d = (e = 42))));

In other words, assignment is right-associative. The last assignment operation is evaluated first, and is propagated leftward through the series of assignments. Specifically: e = 42 assigns 42 to e, then returns e as the result
  1. The value of e is then assigned to d, and then d is returned as the result
  2. The value of d is then assigned to c, and then c is returned as the result
Now, in order to support operator chaining, the assignment operator must return some value. The value that should be returned is a reference to the left-hand side of the assignment. Notice that the returned reference is not declared const. This can be a bit confusing, because it allows you to write crazy stuff like this:
 MyClass a, b, c;
 ...
 (a = b) = c;  //

At first glance, you might want to prevent situations like this, by having operator= return a const reference. However, statements like this will work with primitive types.
It is important to return a non-const reference from your operator=.
General Rule: If it is good enough for ints, then it is good enough for user-defined data-types.

SEMrush Software