Operator Overloading in C++ (ENG)

We introduce operator overloading mainly to address situations where the existing operators do not align with the unique properties of a class. Besides the concept, you will also learn some in-depth details about how it works. Here is what you will learn in this note:

Overloading Syntaxes

C++ provides us a lot of operators, and some special operators like new and new[], delete and delete[], as well as co_await since C++20. Before we go under the hood, let's first take a look at the kinds of operators we have in C++.

Binary Operators

Binary operators operate on two operands. Here are the binary operators in your list:

  • +, -, *, /, % (Arithmetic operators)
  • ^, &, | (Bitwise operators)
  • =, +=, -=, *=, /=, %=, ^=, &=, |=, <<=, >>= (Assignment operators)
  • <, >, <=, >=, ==, !=, <=> (Comparison operators)
  • &&, || (Logical operators)
  • <<, >> (Bitwise shift operators)
  • , (Comma operator)
  • ->*, -> (Member access operators)
  • [], () (Subscript and function call operators)

Unary Operators

Unary operators operate on a single operand. Here are the unary operators in your list:

  • ~ (Bitwise NOT)
  • ! (Logical NOT)
  • ++, -- (Increment and Decrement operators)

Special Operators

Such as new, new[] etc...

Operator Matters

Later, we will create an overloaded operator ourselves. Before that, it's important to understand that different operators play a significant role in operator overloading. We categorized these operators into binary and unary operators, their signatures can vary depending on the operator.

Those signatures match the following:

Expression As member function As non-member function Example
@a (a).operator@ ( ) operator@ (a) ++obj;
a@b (a).operator@ (b) operator@ (a, b) obj3 = obj1 + obj2;
a@ (a).operator@ (0) operator@ (a, 0) obj++;

And be aware, the operators =, (), [], and -> cannot be non-member functions for specific reasons, Copilot says this is because those operators need direct access to the object.

Finally, Easy Examples

This is about Static Polymorphism, Right?

We use the keyword operator to define operator overloading, which allows us to provide custom implementations for operators in user-defined types or classes. When you overload an operator, it behaves exactly like a function with a special name, different parameter means different function symbol. And yes, this is a familiar concept we known as static polymorphism.

Overloading +

#include <iostream>
#include <cstring>
struct vector {
    float x, y, z;

    vector operator+(const vector& rhs) const {
        vector result;
        result.x = this->x + rhs.x;
        result.y = this->y + rhs.y;
        result.z = this->z + rhs.z;
        return result;
    }
};
struct string{
	char* str;
    int str_len;
	string(const char* s = "") {
        str_len = std::strlen(s);
        str = new char[str_len + 1];
        for(int i = 0; i < str_len + 1; i++)
            str[i] = s[i];
    }
    ~string() {
        delete[] str;
    }
	string operator+(const string& other) const {
        int new_length = str_len + other.str_len;
        char* new_str = new char[new_length];
        std::strcpy(new_str, str);
        std::strcat(new_str, other.str);
        return string(new_str);
    
    }
};

int main() {
    vector v1 = {1.0, 2.0, 3.0};
    vector v2 = {4.0, 5.0, 6.0};
    vector v3 = v1 + v2;

    std::cout << "Result: (" << v3.x << ", " << v3.y << ", " << v3.z << ")" << std::endl;
    string s1 = string("hello, ");
    string s2 = string("world!");
    string s3 = s1 + s2;
    std::cout << "Result: " << s3.str << std::endl;
    return 0;
}

Overloading ++

Overloading <<

Overloading new and delete

inline Overloading for Comparison Operators

For operators that are simple to implement, we usually use the inline keyword to instruct the compiler to inline the overloaded operator. We can get several benefits after this:

  • Performance Improvement
  • Code Optimization