Operator Overloading in C++ with some FAQ

In C++, operator overloading allows you to redefine the functionality of the allowed operators, such as “+”, “-“, “=”, “>>”, and “<<“. It is a compile-time polymorphism in which the operator keyword is used for operator overloading.

Operator overloading is a great technique to give special meaning to an existing operator in C++ without changing its original meaning. The compiler distinguishes between the different meanings of an operator by examining the types of its operands.

For example, when you are going to overload the pre and post-operator then for the post-increment you have to pass a dummy int in the overloaded post-increment operator. We will see it in another article on how we can overload pre and post-increment operators.

Now I believe you are thinking that you can overload all C++ operators..

But the answer is No 🙂

C++ allows almost all operators to be overloaded. However, a few operators can not be overloaded in C++.  I have mentioned a few operators that can not be overloaded in C++.

OperatorName
.Member selection
.*Pointer-to-member selection
::Scope resolution
? :Conditional
#Preprocessor convert to string
##Preprocessor concatenate

Note: The sizeof operator can also not overloaded.

Syntax for operator overloading in C++:

//General Syntax for operator overloading


Returntype operator operator_symbol ( parameter-list )

returnType -: is the return type of the function.
operator -: is a keyword.
operator_symbol -: is the operator we want to overload. Like: +, <, -, ++, etc.
parameter-list -: is the arguments passed to the function.

How to write operator overloading in C++:

Overloaded operators are implemented as functions. We need to write the overloaded function name operator x, where x is the operator that allows overload.  For example, to overload the + operator, you define a function called operator+. Like that to overload =, defines a function called operator=.

// Overloading(+) operator to perform increment 

void operator+() 
{ 

  //for example

}

 

Note: In C++, you can redefine the definition of most built-in operators globally or on a class-by-class basis.

 

Example,

In the below example I am overloading the + operator to add two objects of the Test class and return the result and print the same.

#include 
using namespace std;

//class Test
class Test
{
public:
    //constructor
    Test( int data1, int data2 ) : m_data1(data1), m_data2(data2) {}

    Test operator+( Test &rObj);

    //print the value
    void print( )
    {
        cout << "m_data1 = " << m_data1 <<endl;
        cout << "m_data2 = " << m_data2 << endl;
    }

private:
    //member variables
    int m_data1,m_data2;
};



// Operator overloaded using a member function
Test Test::operator+( Test &rObj )
{
    return Test( m_data1 + rObj.m_data1, m_data2 + rObj.m_data2 );
}


int main()
{
    Test obj1(1,2);

    Test obj2(5,6);

    Test obj3(0,0);

    //adding two object of class Test
    obj3 = obj1 + obj2;

    //print the result of addition
    obj3.print();

    return 0;
}

Output:

Operator overloading in c++

In C++ overloaded operators are called implicitly by the compiler when the operators are encountered in the code. But if you want you can call the overloaded operators like the other member or nonmember function.

For example,

obj3 = obj1.operator+(obj2);

Why is operator overloading used?

Let’s see an example before understanding why we should use operator overloading in our program. But you should remember that “if your overloaded operator makes life easier and safer for your users, do it; otherwise don’t”.

#include <iostream>
using namespace std;

//class Test
class Test
{
public:
    //constructor
    Test( int data1, int data2 ) : m_data1(data1), m_data2(data2) {}

    //print the value
    void print( )
    {
        cout << "m_data1 = " << m_data1 <<endl;
        cout << "m_data2 = " << m_data2 << endl;
    }

private:
    //member variables
    int m_data1,m_data2;
};



int main()
{
    Test obj1(1,2);

    Test obj2(5,6);

    Test obj3(0,0);

    //adding two object of class Test
    obj3 = obj1 + obj2;

    //print the result of addition
    obj3.print();

    return 0;
}

Output:

Operator overloading why should use

You can see that if we try to add the two objects (obj1 and obj2) using the inbuilt ‘+’ operator, then we are getting errors. It is because the inbuilt ‘+’ operator allows only for inbuilt types.

It should be clear that this would not make sense to the compiler. ‘Test’ is a programmer-defined type and the compiler does not know how to compute this type. But operator overloading makes this possible.

The C++ programming language allows us to redefine the operator functionality according to use and the way of redefinition is called operator overloading.

So here if you want to add two objects you need to overload the ‘+’ operator. Since operator overloading allows us to change how operators work. You can see at the beginning of the post I have redefined the ‘+’ operator to add two objects.

General Rules for Operator Overloading in C++:

1.  Use your common sense and only overloaded the operators if requires. This is the most important guideline.

2. Operator overloading cannot change the precedence and associativity of operators. However, if we want to change the order of evaluation, parentheses should be used.

3. You cannot redefine the meaning of operators when applied to built-in data types.

4.  Always stick to the operator’s well-known semantics.

5.  An operator function must either be a non-static member function or be a non-member function that has at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration. A non-member function that needs access to private or protected class members must be declared as a friend of that class.

Let’s see a code to understand this point,

#include <iostream>

using namespace std;

class Add
{
public:
    Add(int data):m_data(data)
    {

    }

    // Declare a member operator
    //  overload.
    int operator+( Add &rObj )
    {
        return (m_data+rObj.m_data);
    }

    // Declare addition operators.
    friend int operator+( Add&, int );
    friend int operator+( int, Add& );


private:
    int m_data;
};

//non member function
int operator+( Add& rObj, int data)
{
    return (rObj.m_data+data);
}

//non member function
int operator+( int data, Add& rObj)
{
    return (rObj.m_data+data);
}


int main()
{
    Add obj1(3);
    Add obj2(3);

    cout << obj1+2 <<endl;
    cout << 2 + obj1 <<endl;
    cout << obj2 + obj1 <<endl;

    return 0;
}

Output: 5, 5, 6

7. Unary operators declared as member functions take no arguments; if declared as global functions, they take one argument.

8. Binary operators declared as member functions take one argument; if declared as global functions, they take two arguments.

9. If an operator can be used as either a unary or a binary operator (&, *, +, and -), you can overload each use separately.

10. It is not possible to change the precedence, grouping, or number of operands of operators.

11. The meaning of the operators =, (unary) &, and, (comma), predefined for each type, can be changed for a specific class and enumeration types by defining operator functions that implement these operators.

12. Operator functions are inherited in the same manner as other base class functions.

13. Overloaded operators cannot have default arguments.

Note: For consistency, we must follow the model of the built-in types when defining overloaded operators. If the semantics of an overloaded operator differ significantly from its meaning in other contexts, it can be more confusing than useful.

Examples and constraints on the various categories of overloaded operators in C++

Overloading Unary Operators

A Unary operator is an operator that operates on a single operand. Some of the unary operators are

  1. ! (logical NOT)
  2. & (address-of)
  3. ~ (one’s complement)
  4. * (pointer dereference)
  5. + (unary plus)
  6. - (unary negation)
  7. ++ (increment)
  8. -- (decrement)
  9. conversion operators

A prefix unary operator can be implemented by a non-static member function with no parameters or a non-member function with one parameter.

Thus, to declare any prefix unary operator function as a nonstatic member, you must declare it in the form:

ret_type operator op ()

To declare a prefix unary operator function as a global function, you must declare it in the form:

ret_type operator op ( arg )

where ret_type is the return type and op is one of the prefix unary operators.

If both forms of the operator function have been declared, the function declared as a member takes precedence.

Note: Increment and decrement operators (++ and –)and Conversion operators are also discussed in a separate section ( See the below topics).

Let us see an example, where I am overloading (-) unary operator. Also making unary operator function as a nonstatic member function, so no argument is required. We can overload the unary minus operator (-) operators in many ways.

#include <iostream>
using namespace std;

class Distance
{
private:
    int m_feet;
    int m_inches;

public:
    // constructors
    Distance(int feet, int inches):m_feet(feet),m_inches(inches)
    {

    }

    // method to display distance
    void displayDistance(const char *pObjName)
    {
        cout << pObjName << " = ";
        cout << "Feet: " << m_feet << " Inches:" << m_inches <<endl;
    }

    // overloaded unary operator (-)
    Distance& operator- ()
    {
        m_feet = -m_feet;
        m_inches = -m_inches;
        return *this;
    }
};

int main()
{
    Distance D1(11, -10), D2(-5, 11), D3(0,0);

    //Without performing any operation
    D1.displayDistance("D1");
    D2.displayDistance("D2");
    D3.displayDistance("D3");

    cout << "\n\nResult after Apply negation\n"<<endl;

    D3 =  -D1; // apply negation on D1 and assign to D3
    -D2;   // apply negation on D2
    D1.displayDistance("D1");    // display D3
    D2.displayDistance("D2");    // display D2
    D3.displayDistance("D3");    // display D3

    return 0;
}

Output:

Unary Operator minus

Good News for Aticleworld Reader, One monthly Free Trial Available for you from one of the most popular Learning Platforms. Don’t Waste It.

Increment and Decrement Operator Overloading (C++)

The increment and decrement operators are also unary operator but it falls into a special category because there are two variants of each:

1. Pre-increment and post-increment.

2. Pre-decrement and post-decrement.

The prefix form of the operator is declared exactly the same way as any other unary operator but the postfix form accepts an additional argument of type int.

It means when specifying an overloaded operator for the postfix form of the increment or decrement operator, we must pass an int as an argument.

Example 1: 

Implementation of pre and post-increment as a member function.

class Increment
{
public:
    Increment& operator++(); // prefix ++m_data
    
    Increment operator++(int); // postfix m_data++
private:
    int m_data;
};

 

Example 2: 

Implementation of pre and post-increment as a non-member function.

class Increment
{

};


Increment& operator++(Increment&) // prefix ++
{

}

Increment operator++(Increment&, int)// postfix ++
{

}

 

Note: The int argument will have a value of zero.

 

Let’s see an example code for pre and post-increment where the operator is a member function.

#include <iostream>

using namespace std;

class Increment
{
public:

    Increment(int x):m_data(x)
    {

    }
    Increment& operator++(); // prefix ++m_data

    Increment operator++(int); // postfix m_data++

    // method to display m_data
    void displayValue()
    {
        cout << "m_data: " << m_data <<endl;
    }
private:
    int m_data;
};



Increment& Increment::operator++()// prefix ++m_data
{
    ++m_data;
    return *this;
}

Increment Increment::operator++(int)// postfix m_data++
{
    Increment tmp(0);

    tmp.m_data = m_data++;
    return tmp;
}



int main()
{
    Increment value1(6),value2(27), value3(0);

    //Without performing any operation
    value1.displayValue();
    value2.displayValue();
    value3.displayValue();

    cout << "\nOutput after pre and post increment\n" <<endl;

    //apply per and post increment
    //on respectively value1 and value2

    ++value1;  // value1.operator++();
    value1.displayValue();

    value3 = value2++; // value2.operator++(0);
    value2.displayValue();
    value3.displayValue();

    return 0;
}

Output:

Overloading Binary operators

A Binary operator is an operator that operates on two operands. Some of the binary operators are

OperatorName
,Comma
!=Inequality
%Modulus
%=Modulus/assignment
&Bitwise AND
&&Logical AND
&=Bitwise AND/assignment
*Multiplication
*=Multiplication/assignment
+Addition
+=Addition/assignment
Subtraction
-=Subtraction/assignment
->Member selection
->*Pointer-to-member selection
/Division
/=Division/assignment
<Less than
<<Left shift
<<=Left shift/assignment
<=Less than or equal to
=Assignment
==Equality
>Greater than
>=Greater than or equal to
>>Right shift
>>=Right shift/assignment
^Exclusive OR
^=Exclusive OR/assignment
|Bitwise inclusive OR
|=Bitwise inclusive OR/assignment
||Logical OR

A binary operator can be implemented by a non-static member function with no parameters or a non-member function with one parameter.

Thus, to declare any binary operator function as a nonstatic member, you must declare it in the form:

ret_type operator op (arg)

To declare a binary operator function as a global function, you must declare it in the form:

ret_type operator op ( arg1 , arg2 )

where ret_type is the return type and op is one of the binary operators.

If both forms of the operator function have been declared, the function declared as a member takes precedence.

Example,

In the below example I am overloading the + operator to add two objects of the Distance class and return the result and print the same.

#include 

using namespace std;


class Distance
{
private:
    int m_feet;
    int m_inch;

public:
    // constructors
    Distance(int feet, int inch):m_feet(feet),m_inch(inch)
    {
    }

    // method to display distance
    void displayDistance()
    {
        cout << "Feet: " << m_feet << " Inch: " << m_inch <<endl;
    }

    // overloaded binary operator (+)
    Distance& operator+ (Distance& rObj)
    {
        m_feet = rObj.m_feet + m_feet;
        m_inch = rObj.m_inch + m_inch;
        return *this;
    }
};


int main()
{
    Distance D1(5, 2), D2(7,4), D3(0,0);

    cout << "Value of D1" <<endl;

    //Display value of D1
    D1.displayDistance();

    cout << "\nValue of D2" <<endl;

    //Display value of D2
    D2.displayDistance();

    //Adding D1 and D2
    D3= D1 + D2;

    cout << "\nValue of D3" <<endl;

    //Display value of D3
    D3.displayDistance();

    return 0;
}

Output:

binary operator overloading in c++

Assignment operator overloading C++

The assignment operator (=) is a binary operator. Its declaration is identical to any other binary operator, with the following exceptions:

1. An assignment operator must be implemented as a non-static member function with exactly one parameter.

2.  A copy assignment operator operator= is implicitly declared for a class if not declared by the user ( Default operator= function can be generated by the compiler for class types if does not exists).

3. A base class assignment operator is always hidden by the copy assignment operator of the derived class (Not inherited by derived classes).

Example,

#include 

using namespace std;


class Distance
{
private:
    int m_feet;
    int m_inch;

public:
    // constructors
    Distance(int feet, int inch):m_feet(feet),m_inch(inch)
    {
    }

    // method to display distance
    void displayDistance()
    {
        cout << "Feet: " << m_feet << " Inch: " << m_inch <<endl;
    }

    // overloaded binary operator (+)
    Distance& operator= (Distance& rObj)
    {
        m_feet = rObj.m_feet;
        m_inch = rObj.m_inch;
        return *this;
    }
};


int main()
{
    Distance D1(5, 2), D2(0,0);

    cout << "Value of D1" <<endl;

    //Display value of D1
    D1.displayDistance();

    cout << "\nValue of D2" <<endl;

    //Display value of D2
    D2.displayDistance();

    //Adding D1 and D2
    D2 = D1;

    cout << "\nValue of D2 after assignment" <<endl;

    //Display value of D2
    D2.displayDistance();

    return 0;
}

Output:

Assignment operator c++

Function call overloading C++

The function-call operator is a binary operator and invoked using parentheses. The operator() (function-call operator) must be implemented as a non-static member function with an arbitrary number of parameters. It can have default arguments.

function call syntax:

postfix-expression ( expression-listopt )

where the postfix-expression evaluates to a class object and the possibly empty expression-list matches the parameter list of an operator() member function of the class.

Note: The function-call operator is applied to the name of an object, not the name of a function.

It is important to remember that the function-call operator, when overloaded, does not modify how functions are called. It only modifies how the operator is to be interpreted when applied to objects of a given class type.

Let’s see an example,

#include 

using namespace std;


class Multiplier
{
public:
    Multiplier(int m): m_multiplier(m) {}
    int operator()(int x)
    {
        return m_multiplier * x;
    }

    int operator()(int x, int y)
    {
        return m_multiplier * x *y;
    }

private:
    int m_multiplier;
};


int main()
{
    //creating object
    Multiplier m(2);

    int data = m(4);

    cout << "data = "<< data << endl;

    data = m(2,5);

    cout << "data = "<< data << endl;

    return 0;
}

Output:

data = 8
data = 20

Subscripting overloading C++

The subscript operator ([ ]) is a binary operator and invoked using square brackets. The operator[] (subscript operator) must be implemented as a non-static member function with exactly one parameter. This parameter can be of any type and designates the desired array subscript.

subscript operator syntax:

postfix-expression [ expr-or-braced-init-list ]

Example,

struct X
{
    Z operator[](std::initializer_list);
};

X x;

x[ {1,2,3}] = 7; // OK: meaning x.operator[]({1,2,3})

int a[10];

a[ {1,2,3}] = 7; // error: built-in subscript operator

Class Member Access Operator loading

The operator-> (Class member access) must be implemented as a non-static member function and taking no parameters. 

Class member access syntax:

class-type *operator->()

where class-type is the name of the class to which this operator belongs.

Note: This operator is used (often in conjunction with the pointer-dereference operator) to implement “smart pointers” that validate pointers prior to dereference or count usage.

 

Some FAQ related to operator overloading

What is the difference between operator functions and normal functions?

Operator functions are also the same as normal functions. But the difference is that the operator function must have an operator keyword followed by the operator ‘x, where x is the operator that allows overload.

//operator function 
void operator+() 
{ 
  
}


//Normal function
void add()
{

}

 

Can we overload all operators?

Almost any operator can be overloaded in C++. However, there are few operators that can not be overloaded in C++. The below table contains the operator that can not be overloaded.

OperatorName
.Member selection
.*Pointer-to-member selection
::Scope resolution
? :Conditional
#Preprocessor convert to string
##Preprocessor concatenate

Can I define my own operators in C++?

No, unfortunately, you cannot define new operators—you can only overload existing operators ( allowed operator only).

Can I overload operator== so it lets me compare two char[] using a string comparison?

No: At least one operand of any overloaded operator must be of some user-defined type.

Which is more efficient: i++ or ++i?

++i is sometimes faster than and is never slower than, i++. You can follow this blog post “Pre-increment and Post-increment in C/C++

How can I overload the prefix and postfix forms of operators ++ and --?

What are some guidelines / “rules of thumb” for overloading operators?

Operator Overloading MCQ in C++