Operator overloading is heavily used in math. One of the most famous examples I know is "+". If you add two elements from \(\mathbb{N}\) you will use the same character "+" as you use for adding two numbers from \(\mathbb{R}\). You even use the plus-sign if you add matrices (which is obviously something different than adding single numbers).
In some programming languages, like C++, you can overload operators by yourself.
First simple example
Imagine you wanted to store some data - lets say the prename, surname and age - about people you know. This could be done in a struct
. After you've stored it, you would like to print this information. Obviously, you don't want to do something like this:
for (int i=0; i< 4; i++) {
cout << "Person(" << myArray[i].prename << " "
<< myArray[i].surname << ", " << myArray[i].age << ")";
}
If you wanted to print this information more than one time, you would have to add this long line every time.
A toString() method like the one Java uses would be nice. In C++, you don't have toString, but you can overload the <<
operator!
This is how it works:
#include <iostream>
using namespace std;
typedef struct person {
// attributes
string prename;
string surname;
int age;
// constructor
person(string p, string s, int age) :
prename(p), surname(s), age(age) {}
} Person;
// "toString" for C++
std::ostream& operator<<(std::ostream &strm, const person &a) {
return strm << "Person(" << a.prename << " " << a.surname << ", "
<< a.age << ")";
}
int main(){
Person Martin ("Martin", "Thoma", 22);
Person Andreas ("Andreas", "Thoma", 22);
Person AndiOld ("Andreas", "Berger", 30);
Person AndiYoung ("Andreas", "Berger", 22);
Person myArray[] = {Martin, Andreas, AndiOld, AndiYoung};
for (int i=0; i< 4; i++) {
cout << myArray[i] << endl;
}
return 0;
}
Sorting
You can sort by overloading <
.
You can use a sort by adding
#include <algorithm>
to your program and using sort(array, array + elements);
This is how it looks like:
#include <iostream>
#include <algorithm>
using namespace std;
typedef struct person {
// attributes
string prename;
string surname;
int age;
// constructor
person(string p, string s, int age) :
prename(p), surname(s), age(age) {}
} Person;
// ".equals()" for C++
bool operator<(const Person& a, const Person& b){
if (!(a.prename == b.prename)) {
return a.prename < b.prename;
} else if (!(a.surname < b.surname)) {
return a.surname < b.surname;
} else {
return a.age < b.age;
}
}
// "toString" for C++
std::ostream& operator<<(std::ostream &strm, const person &a) {
return strm << "Person(" << a.prename << " " << a.surname << ", "
<< a.age << ")";
}
int main(){
Person Martin ("Martin", "Thoma", 22);
Person Andreas ("Andreas", "Thoma", 22);
Person AndiOld ("Andreas", "Berger", 30);
Person AndiYoung ("Andreas", "Berger", 22);
Person myArray[] = {Martin, Andreas, AndiOld, AndiYoung};
sort(myArray, myArray + 4);
for (int i=0; i< 4; i++) {
cout << myArray[i] << endl;
}
return 0;
}
By the way, if you don't define <
you get something like this:
In file included from /usr/include/c++/4.4/algorithm:62,
from operators.cpp:2:
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘const _Tp& std::__median(const _Tp&, const _Tp&, const _Tp&) [with _Tp = person]’:
/usr/include/c++/4.4/bits/stl_algo.h:2268: instantiated from ‘void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = Person*, _Size = int]’
/usr/include/c++/4.4/bits/stl_algo.h:5220: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:89: error: no match for ‘operator<’ in ‘__a < __b’
/usr/include/c++/4.4/bits/stl_algo.h:90: error: no match for ‘operator<’ in ‘__b < __c’
/usr/include/c++/4.4/bits/stl_algo.h:92: error: no match for ‘operator<’ in ‘__a < __c’
/usr/include/c++/4.4/bits/stl_algo.h:96: error: no match for ‘operator<’ in ‘__a < __c’
/usr/include/c++/4.4/bits/stl_algo.h:98: error: no match for ‘operator<’ in ‘__b < __c’
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘_RandomAccessIterator std::__unguarded_partition(_RandomAccessIterator, _RandomAccessIterator, _Tp) [with _RandomAccessIterator = Person*, _Tp = person]’:
/usr/include/c++/4.4/bits/stl_algo.h:2268: instantiated from ‘void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = Person*, _Size = int]’
/usr/include/c++/4.4/bits/stl_algo.h:5220: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:2209: error: no match for ‘operator<’ in ‘* __first < __pivot’
/usr/include/c++/4.4/bits/stl_algo.h:2212: error: no match for ‘operator<’ in ‘__pivot < * __last’
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘void std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’:
/usr/include/c++/4.4/bits/stl_algo.h:2178: instantiated from ‘void std::__final_insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:5222: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:2106: error: no match for ‘operator<’ in ‘__val < * __first’
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘void std::__heap_select(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’:
/usr/include/c++/4.4/bits/stl_algo.h:5067: instantiated from ‘void std::partial_sort(_RAIter, _RAIter, _RAIter) [with _RAIter = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:2256: instantiated from ‘void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = Person*, _Size = int]’
/usr/include/c++/4.4/bits/stl_algo.h:5220: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:1906: error: no match for ‘operator<’ in ‘* __i < * __first’
/usr/include/c++/4.4/bits/stl_algo.h: In function ‘void std::__unguarded_linear_insert(_RandomAccessIterator, _Tp) [with _RandomAccessIterator = Person*, _Tp = person]’:
/usr/include/c++/4.4/bits/stl_algo.h:2112: instantiated from ‘void std::__insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:2178: instantiated from ‘void std::__final_insertion_sort(_RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:5222: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_algo.h:2067: error: no match for ‘operator<’ in ‘__val < * __next’
In file included from /usr/include/c++/4.4/bits/stl_algo.h:62,
from /usr/include/c++/4.4/algorithm:62,
from operators.cpp:2:
/usr/include/c++/4.4/bits/stl_heap.h: In function ‘void std::__adjust_heap(_RandomAccessIterator, _Distance, _Distance, _Tp) [with _RandomAccessIterator = Person*, _Distance = int, _Tp = person]’:
/usr/include/c++/4.4/bits/stl_heap.h:394: instantiated from ‘void std::make_heap(_RAIter, _RAIter) [with _RAIter = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:1904: instantiated from ‘void std::__heap_select(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:5067: instantiated from ‘void std::partial_sort(_RAIter, _RAIter, _RAIter) [with _RAIter = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:2256: instantiated from ‘void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = Person*, _Size = int]’
/usr/include/c++/4.4/bits/stl_algo.h:5220: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_heap.h:232: error: no match for ‘operator<’ in ‘*(__first + ((unsigned int)(((unsigned int)__secondChild) * 12u))) < *(__first + ((((unsigned int)__secondChild) + 0xffffffffffffffffffffffffffffffffu) * 12u))’
/usr/include/c++/4.4/bits/stl_heap.h: In function ‘void std::__push_heap(_RandomAccessIterator, _Distance, _Distance, _Tp) [with _RandomAccessIterator = Person*, _Distance = int, _Tp = person]’:
/usr/include/c++/4.4/bits/stl_heap.h:244: instantiated from ‘void std::__adjust_heap(_RandomAccessIterator, _Distance, _Distance, _Tp) [with _RandomAccessIterator = Person*, _Distance = int, _Tp = person]’
/usr/include/c++/4.4/bits/stl_heap.h:394: instantiated from ‘void std::make_heap(_RAIter, _RAIter) [with _RAIter = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:1904: instantiated from ‘void std::__heap_select(_RandomAccessIterator, _RandomAccessIterator, _RandomAccessIterator) [with _RandomAccessIterator = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:5067: instantiated from ‘void std::partial_sort(_RAIter, _RAIter, _RAIter) [with _RAIter = Person*]’
/usr/include/c++/4.4/bits/stl_algo.h:2256: instantiated from ‘void std::__introsort_loop(_RandomAccessIterator, _RandomAccessIterator, _Size) [with _RandomAccessIterator = Person*, _Size = int]’
/usr/include/c++/4.4/bits/stl_algo.h:5220: instantiated from ‘void std::sort(_RAIter, _RAIter) [with _RAIter = Person*]’
operators.cpp:34: instantiated from here
/usr/include/c++/4.4/bits/stl_heap.h:134: error: no match for ‘operator<’ in ‘*(__first + ((unsigned int)(((unsigned int)__parent) * 12u))) < __value’
Equality
You can also define ==
for your structs.
I know this example does NOT make any sense. But it is an example you can work with:
#include <iostream>
using namespace std;
typedef struct person {
// attributes
string prename;
string surname;
int age;
// constructor
person(string p, string s, int age) :
prename(p), surname(s), age(age) {}
} Person;
// "comperator" for C++
bool operator==(const Person& a, const Person& b){
return a.age == 30;
}
int main(){
Person Martin ("Martin", "Thoma", 22);
Person Andreas ("Andreas", "Thoma", 22);
Person AndiOld ("Andreas", "Berger", 30);
Person AndiYoung ("Andreas", "Berger", 22);
Person myArray[] = {Martin, Andreas, AndiOld, AndiYoung};
for (int i=0; i< 4; i++) {
cout << (myArray[i] == myArray[i]) << endl;
}
return 0;
}
Casting
You can also define casts:
#include <iostream>
using namespace std;
typedef struct person {
// attributes
string prename;
string surname;
int age;
// constructor
person(string p, string s, int age) :
prename(p), surname(s), age(age) {}
// prefix
operator int() { return age; }
} Person;
int main(){
Person Martin ("Martin", "Thoma", 22);
Person Andreas ("Andreas", "Thoma", 22);
Person AndiOld ("Andreas", "Berger", 30);
Person AndiYoung ("Andreas", "Berger", 22);
Person myArray[] = {Martin, Andreas, AndiOld, AndiYoung};
for (int i=0; i< 4; i++) {
cout << int(myArray[i]) << endl;
}
return 0;
}
Adding new operators
I like Python very much. Python allows me to get the power of a number like this:
a = 2 ** 10 # 1024
Lets try it for C++:
Doesn't work
#include <iostream>
using namespace std;
// does NOT work
// operators.cpp:7: error: expected initializer before ‘*’ token
int operator**(int a, int b){
int power = 1;
for (int i=0; i < b; i++) {
power *= a;
}
return power;
}
int main(){
cout << 2**10 << endl;
return 0;
}
I guess it doesn't work as it would be very difficult to distinguish something like this:
a = a * *b;
a = a ** b;
If you try to use a $
you get:
operators.cpp:16:13: error: invalid suffix "$10" on integer constant
If you try to use a §
you get:
operators.cpp:7: error: stray ‘\302’ in program
operators.cpp:7: error: stray ‘\247’ in program
operators.cpp:16: error: stray ‘\302’ in program
operators.cpp:16: error: stray ‘\247’ in program
operators.cpp:7: error: expected type-specifier before ‘(’ token
You are also not allowed to redefine *
:
operators.cpp:7: error: ‘int operator*(int, int)’ must have an argument of class or enumerated type
Works
You can wrap the integer like this:
#include <iostream>
using namespace std;
typedef struct integer {
int inner;
// constructor
integer(int i) : inner(i) {}
} Integer;
int operator^(Integer a, Integer b){
int power = 1;
for (int i=0; i < b.inner; i++) {
power *= a.inner;
}
return power;
}
int main(){
cout << (Integer(2)^Integer(10)) << endl; // outputs 1024
return 0;
}
See also
- A class for dealing with fractions - which includes 7 examples for operator overloading
- Operators in C and C++
- The General Syntax of operator overloading in C++. sbi, Stack Overflow.
- The Three Basic Rules of Operator Overloading in C++. sbi, Stack Overflow.
- Overloading operators. C++-Reference.