Operator Overloading in C++: when to use it and why?
What if you can preserve the semantic meaning of operators inside your classes?
For me, one of the best features of a language like C++ is their support of stylistic formatting: they give you the freedom to build a meta-language for your API in simplifying the user experience of people that would want to make transactions with your API -- they are more than just a syntax sugar you put on top, they let you convey meaning succinctly. In other languages like Java, where operator overloading is not supported, you would have to describe the name of the operator instead of their symbols. Consider this:
public class Main {
public static void main(String [] args){
Complex cmpx1 = new Complex(0,3);
Complex cmpx2 = new Complex(1,3);
cmpx1.add(cmpx2);
cmpx1.print();
}
}
class Complex{
private int x;
private int y;
Complex(int x, int y){
this.x = x;
this.y = y;
}
Complex add(Complex obj){
this.x += obj.x;
this.y += obj.y;
return obj;
}
public void print(){
System.out.println(this.x+" + "+this.y + "i");
}
}
Your only means to transact with a Complex
object is to call their functions with the dot
operator, i.e. Complex.add(complexObj)
. Consider the same structure implemented in C++ with operator overloading:
#include <iostream>
class Complex{
private:
int x, y;
public:
Complex (const int &_x = 0, const int &_y = 0){
x = _x;
y = _y;
}
Complex operator+ (const Complex & obj){
Complex result;
result.x = x + obj.x;
result.y = y + obj.y;
return result;
}
friend std::ostream & operator<< (std::ostream & ss, const Complex & obj);
};
std::ostream & operator<< (std::ostream & stream, const Complex & obj){
stream << obj.x << " + " << obj.y << "i";
return stream;
}
int main (){
Complex cmp1 (1, 3);
Complex cmp2 (0, 3);
std::cout << cmp1 + cmp2;
}
Notice that as we simplify the operations in main
, we did a little more specifications within the class Complex
. This is very common in designing software systems, and really shows the sophistication of a language like C++
in this respect. Think of a layered expression in Complex
structure, in Java it would have to look something like this cmp1.add(cmp2.multiply(cmp3.subtract(cmp4)))
where in C++, it would be expressed as cmp1 + cmp2*(cmp3 - cmp4)
. Keep in mind that the C++ compiler imposes operator precedence for evaluating your expressions. In other words, it is not possible to change the precedence, grouping, or number of operands of operators.
Java remains consistent with their style, and they have good reasons for that: to prevent the abuse of notations and building a meta-language on top of their framework because, in some cases, it can be problematic. But we can sort of agree that as long as we keep the semantic meaning of these operators for us, we are not breaking any rules and thereby remain consistent. A good sense to foresee when we are abusing the notation is if another developer would have to look at your implementation of the +
or -
operator, you are probably abusing your notations. We want to make our class easy to use and hard to misuse.
Technical documentation:
But this only scratches the surface with what is possible, check out the technical documentation for more!
- cppeference.com (n.d.). Operator Overloading. en.cppreference.com/w/cpp/language/operators