Operator Overloading: Compile Time Polymorphism in C++

Operator overloading is a compile-time polymorphism in C++ that allows us to make operators work for user-defined data types as objects and structures. In other words, we can extend the meaning of an operator to operate on a user-defined data type.

The main advantage of operator overloading is that we can have different implementations of an operator based on the argument type. For example, we can overload the ‘+’ operator to perform addition on various data types, like integers, strings (concatenation), complex numbers, etc.

Implementing operator overloading

An overloaded operator is called an operator function. It is similar to regular functions, but the name of an operator function is always the operator keyword followed by the operator symbol (like +,-,*, etc.). If we have several overloaded functions of an operator, we can distinguish them by the types and count of operands used with the operator.

class ClassName 
{
    ...
    public
       ReturnType operator OperatorSymbool(argument list) 
       {
            // Implementation logic
       } 
    ...
};
ReturnType: Operator function return type.
operator is a keyword.
OperatorSymbool: operator we want to overload
argument list: the arguments passed to the function.

Operators can be overloaded globally (using the non-member function) or on a class-by-class basis (using the member or friend function). So there are three ways to implement operator overloading:

  • Via member function (member of the class): Operator overloading function can be a member function if the left operand is an object of that class. In this type, unary operators have an empty argument list, and binary operators have a single argument. 
  • Via friend function: The operator overloading function can be made a friend function if it needs access to the private and protected members of a class. In this type, unary operators have a single argument, and binary operators have two arguments.
  • Via non-member function: If the left operand is different, the operator overloading function must be a nonmember function.

Operators that can be overloaded in c++

Rules for operator overloading

These rules that must be considered while overloading operators in C++:

  • Among the operands, at least one operand must be a user-defined datatype.
  • Only built-in operators can be overloaded. In other words, no new operators can be created, only existing operators can be overloaded.
  • The overloaded operators can not have default parameters except the empty parameter list “()”.
  • Operator overloading does not change the precedence and associativity of operators.
  • We can not change the number of operands. Unary operator remains unary, and binary remains binary, etc.
  • In the case of assignment operator =, we do not need to create an operator function. It is already overloaded by default in C++. In other words, the compiler automatically creates a default assignment operator with every class and we can directly use the = operator to copy objects of the same class (An idea similar to the copy constructor!).
  • When we overload operators, we can use them to work in any way. But we need to use operator overloading correctly and consistently to make our code simple to understand.

Adding two complex numbers without using operator overloading

Suppose we want to add two complex numbers without using operator overloading. For this, we create a complex number class Complex and define a public method add(Complex c1, Complex c2) inside it to perform an addition operation. This method will take two complex numbers as an argument and return the resultant complex number after addition.

class Complex 
{
    private:
        int real, imag;
    
    public:
    Complex(int r = 0, int i = 0)
    {
        real = r;
        imag = i;
    }
 
    Complex add(Complex c)
    {
        Complex temp;
        temp.real = real + c.real;
        temp.imag = imag + c.imag;
        return temp;
    }
};
int main()
{
    Complex c1(4, 7);
    Complex c2(3, 5);
    Complex res;
    res = c1.add(c2);
}

Now inside the main method, we create three objects of ComplexNumber class c1, c2, and res, where res is the final instance produced by adding c1 and c2. Now to add c1 and c2, we call the add method with c1 i.e. res = c1.add(c2).

Adding two complex numbers using operator overloading

Suppose we want to overload + operator to add two complex numbers. For this, we define the operator overloading function inside this class (We are redefining the + operator to add complex numbers). 

Now inside the main method, we can add complex number c1 and c2 using a simple instruction: res = c1 + c2, which is equivalent to res = c1.operator+ (c2). This makes our code intuitive and easy to understand.

class Complex 
{
    private:
        int real, imag;
    public:
    Complex(int r = 0, int i = 0) 
    {    
         real = r;   
         imag = i;
    }
    Complex operator + (Complex c) 
    {
         Complex temp;
         temp.real = real + c.real;
         temp.imag = imag + c.imag;
         return temp;
    }
};
 
int main()
{
    Complex c1(4, 7);
    Complex c2(3, 5);
    Complex res;
    res = c1 + c2;   
}

Operator overloading example in c++ by adding two complex numbers

Note: We can also write an operator function like Complex operator + (const Complex& c) in the above code instead of Complex operator + (Complex c). This can be one of the best practices because using & help us reference the c2 object instead of making a duplicate object inside the operator function, and const prevents the operator function from modifying c2.

Another example of overloading addition operator

class opr {
private: 
    int a;
    float b;
    
public:
    opr(int a = 0, float b = 0){
        this->a = a;
        this->b = b;
    }
    
    opr operator +(opr test){
        opr tmp(0,0.0);
        tmp.a = a + test.a;
        tmp.b = b + test.b;
        return tmp;
    }
    
    void show(){
        cout<<a<<" "<<b<<'\n';
    }
};
int main(){
    opr obj1(1, 2.0), 
    opr obj2(2, 1.0);
    opr obj3
    obj3 = obj1 + obj2;
    obj3.show();
    return 0;
}

Operator Overloading in Unary operator

Unary operators operate on only one operand. The increment operator ++ and decrement operator -- are examples of unary operators.

++ (prefix increment) and ++ (postfix increment)

class Value {
private:
    int count;
public:
    Value() : count(2) {}
    
    void operator ++ (){
        ++count;
    }
    
    void operator ++ (int){
        ++count;
    }
    int getCount(){
        return count;
    }
};
int main() {
    Value v;
    v++;
    cout<<v.getCount()<<"\n"
    ++v;
    ++v;
    cout<<v.getCount()<<"\n"
    return 0;
}

NOTE: The only difference in post and prefix operator overloading is the int in the argument list; it doesn’t mean an integer argument. It acts as a signal to the compiler to overload the postfix operator!

Operators that can not be overloaded in c++

Critical ideas to think about!

  • In which situation, do we use operator overloading using the friend function? How do we implement it? Explore some examples.
  • In which situation, do we use operator overloading using the non-member function? How do we implement it? Explore some examples.
  • Why some of the above operators are not overloaded?

Thanks Ankit for his contribution to creating the first version of the draft. Enjoy learning, Enjoy oops!

Share feedback with us

More blogs to explore

Our weekly newsletter

Subscribe to get weekly content on data structure and algorithms, machine learning, system design and oops.

© 2022 Code Algorithms Pvt. Ltd.

All rights reserved.