11 Operator Overloading
Jyoti Pareek
Introduction
Constructs like structure and class allow us to create our own data type. For example if we want to store date, we can create appropriate data type by creating class Time as follows:
#include<iostream>
#include<conio.h>
using namespace std;
class Time
{
private:
int second;
int minute;
int hour;
public:
Time();
Time(int h, int m,int s);
void displayTime();
void readTime();
Time addTime(Time t);
Time subtractTime(Time t );
};
This class will allow us to store time in a single variable like
Time now;
Which otherwise would have required 3 integer variables for the same such as int hrs, min sec;
C++ by means of providing class construct empowers us to create our own data type, but the data type thus created is inferior to standard data type in many ways:
- We cannot use operators provided by the language with the user defined data types to perform arithmetic, logical or other operations. For example the difference of two Time objects end_time and start_time cannot be computed using arithmetic operator (–) minus. We need to do the same as follows
time_diff = end_time.subtractTime(start_time) ;
The above statement achieves the goal but it is not as readable and clearas the following statement.
time_diff = end time – start_time ;
The above statement though very clear and readable, is not valid for the Time class that we have defined.
- We cannot read the objects of our class through cin and cout object as we read variable of standard data type. For example we read and write integer variable x as
cin >> x ;
cout << x ;
But we cannot read or write object of Time class as:
Time now;
cin >> now ;
cout << now ;
For reading and writing Time object we will write following statements
Time now;
now.readTime() ;
now.displayTime() ;
- Implicit type conversion is not done for the user defined data types. For example we cannot assign an integer value to our Time object as C++ does not provide implicit conversion of integer to Time object
Time current_time;
current_time = 10 ;
// This will not work
We cannot be content with the fact that data type defined by us is inferior to standard data type. Well we do not need to be. The good news is that with little more work, it is possible to modify our class to compensate for the said deficiencies. For the first two shortcomings the desired can be achieved with the addition of appropriately overloaded operators to our class, which is the subject of this module and the next module. The third deficiency can be overcome by providing appropriate constructors and conversion functions for our class. That we will discuss in later module.
Operator Overloading
Operator overloading means overloading existing operators with more tasks. For example operations such as subtraction of integers and subtraction of real numbers have already been defined for minus (–) operator, we can define another task of subtracting two time objects for minus (–) operator. This process of overloading operator with one more task is called operator overloading. We can overload unary as well as binary operators. Let us see how we can overload operators.
Overloading Binary Operator
C++ allows us to overload an operator with the help of special function that we can add to our class. The syntax of function for overloading binary operator is given below
return_type operator operator_to_be_overloaded (class object)
For our Time class we can overload the minus (– ) operator with yet another job, that is of finding difference of two Time objects. We can overload minus (–) operator for finding difference of two Time objects as follows:
Time Time :: operator- ( Time t )
{
Time time;
int seconds_1 , seconds_2 , seconds_diff ;
// convert time into second
seconds_1 = (hour *3600) + (minute * 60) + second ; seconds_2 = (t.hour * 3600) + (t.minute * 60) + t.second ;
seconds_diff = seconds_1 – seconds_2 ;
time.hour = seconds_diff / 3600 ;
seconds_diff = seconds_diff % 3600 ;
time.minute = seconds_diff /60 ;
seconds_diff = seconds_diff % 60 ;
time.second = seconds_diff ;
return time;
}
Once the above function is added to our class the following statement would work fine time_taken = end_time – start_time;
Here time_taken, end_time and start_time are objects of Time class. Above statement would result in invoking overloaded minus (–) operator function through end_time object, and start_time object will be passed as argument. The object containing the difference of time will be returned by the overloaded function, which will be stored in the variable time_taken.
Operators can also be overloaded for adding objects of other data type with the object of our class. For example minus (–) operator can also be overloaded to make the following statement work.
Time_difference = current_time – 5;
To make this work, we need to overload minus (–) operator as follows
Time Time :: operator– ( int h )
{
Time diff; | |
diff.hours | = hours – h; |
diff.minutes = minutes; | |
diff.seconds | = seconds; |
return diff; | |
} |
Let us see few more binary operators that can be overloaded for our Time class
Overloading Relational Operator
Overloading == operator
int Time::operator==(Time t)
{
if(hour==t.hour && minute==t.minute && seconds==t.seconds)
return 1;
else
return 0;
}
Overloading > operator
int Time :: operator >( Time t )
{
if( hour > t.hour )
return 1;
else I f( hour == t.hour)
{
If (minute > t.minute )
return 1;
else I f( minute == t.minute )
{
If (seconds>t.seconds)
return 1;
else
return 0;
}
else
return 0;
}
else
return 0;
}
We can add above two functions to our Time class to have following code work for us
Time t1 , t2 ;
If ( t1 > t2 )
Cout << “Time T1 is Greater”;
else if ( t1 == t2)
cout << “Both time are equal” ;
else
cout << “Time T2 is greater” ;
Here overloaded > and == operator functions will be invoked on object t1 and object t2 will be passed as argument.
Overloading shorthand operators
We can also overload shorthand operators. Following function will overload shorthand operator (-=).
void Time :: operator-= ( Time t )
{
Time time;
int seconds_1 , seconds_2 , seconds_diff ;
seconds_1 = (hour *3600) + (minute * 60) + seconds ; seconds_2 = (t.hour * 3600) + (t.minute * 60) + t.seconds ;
seconds_diff = seconds_1 – seconds_2 ;
hour = seconds_diff / 3600 ;
seconds_diff = seconds_diff % 3600 ;
minute = seconds_diff /60 ;
seconds_diff = seconds_diff % 60 ;
seconds = seconds_diff ;
}
We can add above function to our Time class to have following code work for us
Time t1, t2;
t1 -= t2;
Here overloaded (-=) operator function will be invoked on object t1 and object t2 will be passed as argument. Function will update t1 object with computed difference of Time objects t1 and t2.
Overloading array subscript operator []
It is interesting to see how operators like comma operator, function call operator (), array subscript operator [] can be meaningfully overloaded for user defined data types. Let us see how array subscript operator can be meaning fully overloaded for our Time class.
int Time :: operator []( char unit )
{
switch(unit)
{
case ‘h’ :
case ‘m’ :
case ‘s’ :
return hour;
return minute;
return seconds;
}
}
With the above function added to our class, we can get the hours, minutes and seconds of Time class objects printed as follows
cout<<“\n T1 Second is =”<<t1[‘s’];
cout<<“\n T1 Minute is =”<<t1[‘m’];
cout<<“\n T1 Hour is =”<<t1[‘h’];
Here object t1 will invoke the function and character ‘h’/’ m’/’s’ will be passed as argument. The function will return hours, minutes or seconds stored in t1 depending on the character passed.
Operator Functions
We can also overload function call operator () for any class. Once we overload this operator, use of operator () with the object of the class will look like a function call. For example we can overload function call () operator on Matrix class to get the value stored on a specific row and column as follows
class Matrix
{
int mat[20][20];
int row, column;
public :
…………..
…………
int operator() ( int r, int c);
{
return mat[r][c];
}
……………..
……………..
};
With the overloaded operator () for class Matrix, we can get the value of any matrix element as follows:
Matrix m; | // element stored on 4th row and 5th column of matrix m |
cout << m(4,5); | |
will be printed |
Such objects are known as function objects. Here overloaded operator function will be invoked on object m and row and column will be passed as argument.
Overloading Unary Operator
We can also overload unary operators such as (++) and (–). The syntax of function for overloading unaryoperator is given below
return_type operator operor_to_be_overloaded ();
For example operator function for overloading unary operator ++, when overloaded as member function will have following prototype:
Time operator ++ ( );
It does not require any argument as unary operator has only one operand, which will be the invoking object, hence will be passed implicitly to the function.
Overloading Increment and decrement operators
C++ provides increment and decrement operator in two flavors – Postfix version & Prefix version
Time now;
now++;
++now;
If we want both the versions of increment operator (++) to be overloaded, the prototype for both the operator functions will turn out to be identical. It will result into ambiguous situation for the compiler. To resolve this, C++ compiler allows us to define different prototypes for both the versions. To overload prefix version, prototype of operator function will be as follows:
Time operator++ ( );
and to overload postfix version of increment operator (++) function prototype will be as follows:
Time operator++ (int x);
Here x is a dummy argument. It has no role in the function and has been defined only to distinguish between infix and postfix versions of (++) operator.
Following is the example of prefix and postfix increment (++) operator overloading:
//prefix version
void Time :: operator ++( )
{
//increment seconds
seconds++ ;
if(seconds==60)
{
seconds=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
}
}
}
//postfix version
void Time :: operator ++( int x )
{
//increment seconds
seconds++ ;
if(seconds==60)
{
seconds=0;
minute++;
if(minute==60)
{
minute=0;
hour++;
}
}
Both the versions for decrement operator (–) can also be overloaded in the similar fashion.
Operators that cannot be overloaded
Barring few operators almost all operators can be overloaded. List of operators which cannot be overloaded is given below:
1. Dot operator for member access (.)
2. Dereference member to class operator (.*)
3. Scope resolution operator (::)
4. Size of operator (sizeof)
5. Conditional ternary operator (?:)
6. Casting operator (static_cast<>, dynamic_cast<>, reinterpret_cast<>, and const_cast<>)
7. # and ## tokens for macro preprocessors
One may ask why these operators cannot be overloaded. Consider overloading operator(.), which will give new meaning to member access function. This may result in creating ambiguous scenario for compiler – which can either result into serious errors or make compiler’s job very complex.
Operator overloading as such is a complex problem to be managed by compiler, because of which perhaps many object oriented programming languages including JAVA do not provide operator overloading. Therefore this much liberty of not allowing few operators to be overloaded by C++ compiler perhaps is justified.
Summary
By means of operator overloading we can use operators provided by the language, with the user defined data types to perform arithmetic, logical or other operations. Operator overloading is not essential feature for any object oriented programming language. As discussed before many object oriented programming languages including JAVA do not provide operator overloading. We can do without operator overloading but operator overloading provides significant advantages in terms of making code easy to read and manage (a very important quality factor for any program). It reduces the burden of programmer by eliminating the need to remember the function name and as name of function is not required; chances of spelling mistake is also not there. Also in context of C++ , overloading operators such as assignment operator = appropriately, resolve issues arising from scoping rules in certain cases. We will discuss this in detail in later module.
However operator overloading will yield full advantage when the new definition added to the operator is intuitive resulting into simple and easy to understand code.
Following is the complete C++ program for overloading operators discussed above.
you can view video on Operator Overloading |
Additional References
1) Stanley Lippmann, “C++ Primer”, Pearson Education.
2) Bjarne Stroustrup, “The C++ Programming Language”, Pearson Education.
3) Scott Mayer, “Effective C++”, Addison Wesley.
4) Bhushan Trivedi , Programming with ANSI C++, 2/e , Oxford University Press
5) Yashavant P. Kanetkar “Let Us C++” , Bpb Publications.
6) Abhiram G. Ranade, “An Introduction to Programming through C++” , McGraw Hill
7) Ellis and B. Stroustrup “Annotated C++ Reference Manual”, http://www.stroustrup.com/arm.html
8) Herbert Schildt, “Complete Reference C++”, McGraw Hill Publications.
9) Ashok Kamthane, “Object Oriented Programming with ANSI and Turbo C++”, Pearson Education
10) E Balaguruswami, “Object Oriented Programming With C++”, Tata McGraw Hill
11) “C++ FAQs”, Pearson Education.