Operator overloading helps to enable a different syntax to call functions. It helps to make programs easy to read and understand. Specifically, the only difference with functions is that in the normal function call, the parameters are enclosed in parenthesis. Whereas, with operator calls, parameters are placed in the position of their operands. The case of overloading unary operators is special as there is only one operand or parameter present.
This post explains overloading of unary ++ (or — ) operators. In C++, there are 2 ways to call them, one is Prefix (++a) increment and Postfix (a++) increment. Each type of increment shall indeed invoke a different operator overload function.
When compiler sees Prefix increment (++a), it looks for a match of type operator++( ). And when the compiler sees Postfix increment a++, it then calls operator++(int).
The following example demonstrates a simple operator overloading program.
#include <iostream> //main header
using namespace std; //namespace
class IntClass
{
int ival;
public:
IntClass() : ival(0) {}
IntClass(int xvar) : ival(xvar) {}
void printing() {
cout << ival << endl;
}
IntClass& operator++() //pre-increment
{
cout << "Pre-increment" << endl;
ival++;
return *this;
}
const IntClass operator++(int) //post-increment
{
cout << "Post-increment" << endl;
IntClass old(ival);
ival++;
return old;
}
};
int main()
{
IntClass ivar(4);
ivar.printing();
ivar++; //Post-increment
ivar.printing();
++ivar; //Pre-increment
ivar.printing();
return 0;
}
Output
4
Post-increment
5
Pre-increment
6
Explanation:
The signature of overload operator is :
IntClass& operator++() //pre-increment
const IntClass operator++(int) //post-increment
There are few important things to consider. Firstly, the compiler never uses the integer argument in post-increment version. This value just differentiates the 2 overloads for compiler. The compiler passes a dummy value during call.
Please note that compiler expects only an integer argument in postfix version. When the operator function uses something else, then compiler do not accept it.
The following example provides a float argument instead of int
const IntClass operator++(float) //post-increment
{
IntClass old(ival);
ival++;
return old;
}
The compiler generates following error :
Postfix version returns temporary object
Secondly, the return value of postfix overloads is const. This is because, this function creates an intermittent object with previous value and then returns a temporary object with previous value. Definitely, no one should change a temporary object. Otherwise following expressions would become legal
ivar++++; //2-times post increment illegal
ivar++++++; //3-times post increment illegal
This is same as
ivar.operator++(0).operator++(0);
ivar.operator++(0).operator++(0).operator++(0);
Problem with temporary object
In case, the postfix version do not return a const object, like, as shown in the example. Then it will allow manipulation of returned object which is not logical. The temporary object do not exist any more and there is no point to change its value.
In following example, the temporary value is changed because the postfix do not return const object.
#include <iostream> //main header
using namespace std; //namespace
class IntClass
{
int ival;
public:
IntClass(int xvar) : ival(xvar) {}
void printing() {
cout << ival << endl;
}
void add_two() {
ival += 2;
cout << "Value=" << ival << endl;
}
IntClass operator++(int) //post-increment,
//no const return
{
cout << "Post-increment" << endl;
IntClass old(ival);
ival++;
return old;
}
};
int main()
{
IntClass ivar(4);
(ivar++).add_two(); //Manipulates temporary
ivar.printing();
return 0;
}
The Output is:
Post-increment
Value=6
5
Which one is efficient – Pre or Post ?
Finally, the return value of pre-increment contains an ampersand, hence, this is more efficient than postfix counterpart. This is because, internally no extra temporaries needed.
Implementation of pre & post version as friend functions
The following example demonstrates second way to implement pre and post version of ++ operator.
#include <iostream> //main header
using namespace std;//namespace
class IntClass
{
int ival;
public:
IntClass() : ival(0) {}
IntClass(int xvar) : ival(xvar) {}
void printing()
{
cout << ival << endl;
}
friend const IntClass& operator++(IntClass& avar);
//pre-increment
friend const IntClass operator++(IntClass& avar, int);
//post-increment
};
const IntClass& operator++(IntClass& avar)
//pre-increment
{
cout << "Pre-increment" << endl;
avar.ival++;
return avar;
}
const IntClass operator++(IntClass& avar, int)
//post-increment
{
cout << "Post-increment" << endl;
IntClass old(avar.ival);
avar.ival++;
return old;
}
int main()
{
IntClass ivar(4);
ivar.printing();
ivar++;
ivar.printing();
++ivar;
ivar.printing();
return 0;
}
4
Post-increment
5
Pre-increment
6