How NOT to overload C++ operators
Basics
When you create a new class it’s often useful to re-define the operators (“+” for example) so that they work with your new class. Some ways to do this are better than others. This document assumes that you can sometimes learn more from mistakes than perfection. Let’s start simply by creating a class that contains only an integer.
// Version 1 #include <iostream> using namespace std; class anint { public: int i; // 2 constructors anint() { i=0; } anint(int a) { i=a; } }; int main() { anint i(2), j(3), k; k=4; }
In i
and j
are created using the constructor that takes one integer. k
is created using the constructor that takes no arguments. That “k=4;
” works may come as a surprise, but in this situation, C++ uses the constructor that takes one integer to create a temporary aninit
that is copied into k
. If you don’t want C++ to be so liberal, you can put explicit
before “ anint(int a)
“.
Adding object to object
One way to add 2 objects of this new class together is to try this
// Version 2 #include <iostream> using namespace std; class anint { public: int i; // 2 constructors anint() { i=0; } anint(int a) { i=a; } anint operator+(const anint& v) { anint result; result.i = this->i + v.i; return result; } }; int main() { anint i(2), j(3), k; k=i+j; cout << "k.i=" << k.i << endl; }
This works. The new member function is invoked whenever 2 objects of type anint
are added. However, if you add
i+j=k;
to main
you’ll find that the compiler doesn’t complain. That’s not good news. The fix is to put const
at the start of the anint operator+ ...
line.
Adding int to object
Suppose that you wanted
i=j+7;
to compile. It’s a reasonable thing to try. If you replace the i+j=k
line by i=j+7
it compiles, and produces the right answer (because the 7 is converted to an anint
). However, if you replace i=j+7
by i=7+j
it fails with an error message something like
error: no match for 'operator+' in '7 + j'
The problem is that the member function only comes into play when the first operand is of type anint
. The parameter of the function describes what type the 2nd operand should be. To cope with the situation where the first operand isn’t of type anint
, the function has to be outside of the class. Here’s some code –
// Version 3 #include <iostream> using namespace std; class anint { public: int i; // 2 constructors anint() { i=0; } anint(int a) { i=a; } const anint operator+(const anint& v) { anint result; result.i = this->i + v.i; return result; } }; // This function adds an int and an anint when the int is first. const anint operator+(const int& v1,const anint& v2 ) { anint result; result.i = v1 + v2.i; return result; } int main() { anint i(2), j(3), k; k=7+j; cout << "k.i=" << k.i << endl; }
Friends and incrementing
In our anint
the integer is public, which isn’t a good idea in general. Let’s make it private
class anint { private: int i; public: // 2 constructors anint() { ...
Unfortunately that means that the non-member function operator+
can no longer access the i
. We seem to be stuck – whether the function’s a member function or not we have problems. Fortunately there are ways out. By making the non-member function a friend
of the class, it will have access to all the class members. Here’s the code. As a bonus there’s a new +=
function and an output friend function too
// Version 4 #include <iostream> using namespace std; class anint { friend anint operator+(const int& v1,const anint& v2 ); friend ostream &operator <<(ostream &stream, const anint v); private: int i; public: // 2 constructors anint() { i=0; } anint(int a) { i=a; } const anint operator+(const anint& v) { anint result; result.i = this->i + v.i; return result; } anint& operator+=(const int& v) { this->i+=v; return *this; } // preincrement - faster than postincrement because it doesn't // need to create a copy of the original object in order to return it anint operator++() { ++i; return *this; } anint operator++( int ){ anint tmp= *this; ++i; return tmp; } }; // This function adds an int and an anint when the int is first. const anint operator+(const int& v1,const anint& v2 ) { anint result; result.i = v1 + v2.i; return result; } ostream &operator <<(ostream &stream, const anint v) { cout << v.i; return stream; }; int main() { anint i(2), j(3), k; k=7+j; cout << "k=" << k << endl; k+=2; cout << "k=" << k << endl; ++k; cout << "k=" << k << endl; }