Operator Overloading
The feature of C++ which allows programmers to redefine the meaning of operators to make
them work with classes and objects is operator overloading.
While evaluating expressions, C++ compiler checks whether the operands are of normal
types or user-defined types. If the operands are of built-in or normal type, it performs the
regular operation. If the operands are of user-defined type, compiler checks if there is a
function available with an operator or not. If no such function is available, it throws an error.
Operator overloading is another type of compile-time polymorphism like function
overloading and constructor overloading. Operator overloading is provided by the language,
programmer, or both.
Advantages of Operator Overloading
Following are the advantages of operator overloading:
Operator overloading enables programmers to use notation closer to the target
domain. For example we can add two matrices by writing M1 + M2 rather than
writing [Link](M2).
Operator overloading provides similar syntactic support of built-in types to user-
defined types.
Operator overloading makes the program easier to understand.
Rules for Operator Overloading
Following are the rules for overloading operators in C++:
1. Only built-in operators can be overloaded.
2. Degree or arity of the operators cannot be changed.
3. Precedence and associativity of the operator cannot be changed.
4. Overloaded operator cannot have default arguments, except for () operator.
5. At least one operand must be of user-defined type.
6. =, [], (), -> must be defined as member functions. Remaining operators can be either
member or non-member functions.
7. Some operators like =, &, and comma operator are already overloaded by default.
8. Operators like ::, dot, and ?: cannot be overloaded.
Syntax for Operator Overloading
An operator that is to be overloaded is declared in the public section. The syntax for an
operator overloading function is as follows:
class ClassName
{
...
public:
...
return-type operator op(params-list)
{
//body of the function
...
}
...
};
In the above syntax, op is the operator to be overloaded and operator is a keyword.
For example, the function for overloading + operator for adding two complex numbers will
be as follows:
Complex operator +(Complex &c2)
{
Complex temp;
[Link] = real + [Link];
[Link] = imag + [Link];
return temp;
}
Now, if c1 and c2 are two objects of Complex class, we can add them by writing c3 = c1+ c2.
Complete program for adding two complex numbers is as follows:
#include <iostream>
using namespace std;
class Complex
{
private:
float real;
float imag;
public:
Complex() {}
Complex(float a, float b)
{
real = a;
imag = b;
}
Complex operator +(Complex &c2)
{
Complex temp;
[Link] = real + [Link];
[Link] = imag + [Link];
return temp;
}
void show_details()
{
cout<<real<<"+i"<<imag;
}
};
int main()
{
Complex c1(5, 4);
Complex c2(3, 1);
Complex c3 = c1 + c2;
c3.show_details();
return 0;
}
Output of the above program is as follows:
8+i5
Operators that cannot be Overloaded
Although most of the operators in C++ can be overloaded, there are some operators which
cannot be overloaded. They are: scope resolution operator (::), member selection operator
(.) and ternary operator (?:). Following are some points to remember about operator
overloading:
Operator overloading extends the semantics without changing its syntax.
Some operators like assignment operator (=) and address operator (&) are already
overloaded by C++.
Operator overloading does not alter the precedence and associativity of operators.
New operators cannot be created using operator overloading.
When operators such as &&, || are overloaded, they lose their special properties of
short-circuit evaluation and sequencing.
Overloaded operators cannot have default arguments.
All overloaded operators except the assignment operator are inherited by the derived
class.
Number of operands cannot be changed for an operator using operator overloading.
Implementing Operator Overloading
Operator overloading can be implemented in two ways. They are:
1. Using a member function
2. Using a friend function
Differences between using a member function and a friend function to implement operator
overloading are as follows:
Overloading Unary Operators
Operators which work on a single operand are known as unary operators. Examples are:
increment operator(++), decrement operator(–), unary minus operator(-), logical not
operator(!) etc.
Using a member function to overload an unary
operator
Following program demonstrates overloading unary minus operator using a member function:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number(int x)
{
this->x = x;
}
void operator -()
{
x = -x;
}
void display()
{
cout<<"x = "<<x;
}
};
int main()
{
Number n1(10);
-n1;
[Link]();
return 0;
}
Output of the above program is as follows:
x = -10
In the above program the value of x is changed and printed using the function display(). We
can return an object of class Number after modifying x as shown in the following program:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number(int x)
{
this->x = x;
}
Number operator -()
{
x = -x;
return Number(x);
}
void display()
{
cout<<"x = "<<x<<endl;
}
};
int main()
{
Number n1(10);
Number n2 = -n1;
[Link]();
return 0;
}
Output of the above program is as follows:
x = -10
Using a friend function to overload an unary operator
When a friend function is used to overload an unary operator following points must be taken
care of:
The function will take one operand as a parameter.
The operand will be an object of a class.
The function can access the private members only though the object.
The function may or may not return any value.
The friend function will not have access to the this
Following program demonstrates overloading an unary operator using a friend function:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number(int x)
{
this->x = x;
}
friend Number operator -(Number &);
void display()
{
cout<<"x = "<<x<<endl;
}
};
Number operator -(Number &n)
{
return Number(-n.x);
}
int main()
{
Number n1(20);
Number n2 = -n1;
[Link]();
return 0;
}
Output of the program is as follows:
x = -20;
Overloading prefix operators
The syntax for overloading prefix increment and decrement operators is as follows:
return-type operator ++( )
{
//Body of the function
...
}
Following program demonstrates overloading prefix increment and decrement operators:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number(int x)
{
this->x = x;
}
Number operator ++()
{
x = x + 1;
return Number(x);
}
Number operator --()
{
x = x - 1;
return Number(x);
}
void display()
{
cout<<"x = "<<x<<endl;
}
};
int main()
{
Number n1(20);
Number n2 = ++n1;
[Link]();
Number n3(20);
Number n4 = --n3;
[Link]();
return 0;
}
Output of the above program is as follows:
x = 21
x = 19
Overloading postfix operators
When overloading postfix increment and decrement operators we have to include an extra
integer parameter to avoid confusion with prefix operators. Syntax for overloading postfix
increment operator is as follows:
return-type operator ++(int)
{
//Body of function
...
}
The int parameter is a dummy parameter and there is no need to give any name for it.
Following program demonstrates overloading postfix increment and decrement operators:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number(int x)
{
this->x = x;
}
Number operator ++(int)
{
return Number(x++);
}
Number operator --(int)
{
return Number(x--);
}
void display()
{
cout<<"x = "<<x<<endl;
}
};
int main()
{
Number n1(20);
Number n2 = n1++;
[Link]();
[Link]();
Number n3 = n2--;
[Link]();
[Link]();
return 0;
}
Output of the above program is as follows:
x = 21
x = 20
x = 20
x = 19
Overloading Binary Operators
As unary operators can be overloaded, we can also overload binary operators. Syntax for
overloading a binary operator using a member function is as follows:
return-type operator op(ClassName &)
{
//Body of function
...
}
Syntax for overloading a binary operator using a friend function is as follows:
return-type operator op(ClassName &, ClassName &)
{
//Body of function
...
}
Following program demonstrates overloading the binary operator + using a member function:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number() {}
Number(int x)
{
this->x = x;
}
Number operator +(Number &n)
{
Number temp;
temp.x = x + n.x;
return temp;
}
void display()
{
cout<<"x = "<<x<<endl;
}
};
int main()
{
Number n1(20);
Number n2(10);
Number n3 = n1 + n2;
[Link]();
return 0;
}
Output of the above program is as follows:
x = 30
Following program demonstrates overloading the binary operator + using a friend function:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number() {}
Number(int x)
{
this->x = x;
}
friend Number operator +(Number &, Number &);
void display()
{
cout<<"x = "<<x<<endl;
}
};
Number operator +(Number &n1, Number &n2)
{
Number temp;
temp.x = n1.x + n2.x;
return temp;
}
int main()
{
Number n1(20);
Number n2(10);
Number n3 = n1 + n2;
[Link]();
return 0;
}
Output of the above program is as follows:
x = 30
Note: Operators such as =, ( ), [ ], and -> cannot be overloaded using friend functions.
Overloading Special Operators
Some of the special operators in C++ are:
new – used to allocate memory dynamically
delete – used to free memory dynamically
( ) and [ ] – subscript operators
-> – member access operator
Let’s see how to overload them.
Overloading new and delete operators
C++ allows programmers to overload new and delete operators due to following reasons:
To add more functionality when allocating or deallocating memory.
To allow users to debug the program and keep track of memory allocation and
deallocation in their programs.
Syntax for overloading new operator is as follows:
void* operator new(size_t size);
Parameter size specifies the size of memory to be allocated whose data type is size_t. It
returns a pointer to the memory allocated.
Syntax for overloading delete operator is as follows:
void operator delete(void*);
The function receives a parameter of type void* and returns nothing. Both functions for new
and delete are static by default and can’t access this pointer. To delete an array objects, the
operator delete[ ] must be overloaded.
Following program demonstrates overloading both new and delete operators:
#include <iostream>
using namespace std;
class Number
{
private:
int x;
public:
Number(int x)
{
this->x = x;
}
void* operator new(size_t size)
{
void *ptr = ::new int[size]; //Using global new operator
cout<<"Memory allocated of size: "<<size<<endl;
return ptr;
}
void operator delete(void *ptr)
{
::delete(ptr); //Using global delete operator
cout<<"Memory deallocated"<<endl;
}
void display()
{
cout<<"x = "<<x<<endl;
}
};
int main()
{
Number *n = new Number(10); //Invokes overloaded new operator
n->display();
delete n; //Invokes overloaded delete operator
return 0;
}
Output of the above program is as follows:
Memory allocated of size: 4
x = 10
Memory deallocated
In the above program, ::new and ::delete refers to the global new and delete operators.
When new is called, compiler executes the overloaded function for new and also
automatically calls the constructor.
Advantages of overloading new and delete operators
The overloaded new operator function can receive one or more parameters. This
allows flexibility in customizing memory allocation.
Overloaded delete operator provides garbage collection for objects of classes.
Programmers can add exception handling code while allocating memory.
Programmers can use memory management functions like malloc(),
realloc(), and free() inside the overloaded functions of new and delete
Overloading Comma Operator (,)
The comma operator (,) can be overloaded to define custom behavior when evaluating
expressions involving objects. Normally, the comma operator evaluates the first expression,
discards the result, and then evaluates the second expression. By overloading it, we can
specify what happens when it is used between objects of a class.
Syntax:
return-type operator , (ClassName &obj) {
// body of function
}
Example:
#include <iostream>
using namespace std;
class Test {
int x;
public:
Test(int a = 0) { x = a; }
Test operator ,(Test &obj) {
Test temp;
temp.x = this->x + obj.x;
return temp;
}
void show() { cout << "x = " << x << endl; }
};
int main() {
Test t1(10), t2(20), t3;
t3 = (t1, t2); // Calls overloaded comma operator
[Link]();
return 0;
}
Output:
x = 30
In this example, the overloaded comma operator adds the data members of two objects and
returns a new object containing the result.