C++ Tutorial
C++ Operator Overloading
You can define + or << for your types so expressions read like math.
Operators for your types
Built-in types already know + and <<. int adds.ostream prints. A type you wrote does not, until you say how. Operator overloading is that definition: a function named operator+ or operator<< that the compiler calls when it sees the symbol.
Use it when the symbol already has a clear meaning. Adding two points should move by both offsets. Printing a point should show both coordinates. Do not invent a private language of operators.
Add two points
A Point holds x and y. operator+ as a member takes the other point, builds a third, and returns it. The left operand is this object. The right operand is the parameter. Neither original point changes.
Example
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
Point operator+(Point other) {
Point sum;
sum.x = x + other.x;
sum.y = y + other.y;
return sum;
}
};
int main() {
Point a;
a.x = 2;
a.y = 3;
Point b;
b.x = 4;
b.y = 1;
Point c = a + b;
cout << c.x << " " << c.y << endl;
return 0;
}Output is 6 4. The line Point c = a + b; is the call a.operator+(b).
Open the listing with Try it in C++. That is /cpp/try, compiled by g++. Change the coordinates and run again.
The result is a Point
Because + returns a Point, you can add again. a + b + d first buildsa + b, then adds d to that temporary. Each step is the same function.
Example
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
Point operator+(Point other) {
Point sum;
sum.x = x + other.x;
sum.y = y + other.y;
return sum;
}
};
int main() {
Point a;
a.x = 1;
a.y = 0;
Point b;
b.x = 2;
b.y = 5;
Point d;
d.x = 3;
d.y = -1;
Point total = a + b + d;
cout << total.x << " " << total.y << endl;
return 0;
}Output is 6 4 again. If you need to change a point in place, write a named function such asmove_by. Keep + for a new value, the same way 2 + 3 does not rewrite2.
Print with operator<<
cout << n works because << is overloaded for integers. ForPoint, define a free function named operator<<. The left side is the stream. The right side is the point. Return the stream so << endl still chains.
Fields on this struct are public, so the function does not need friend. It readsp.x and p.y the same way main would.
Example
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
ostream& operator<<(ostream& out, Point p) {
out << "(" << p.x << ", " << p.y << ")";
return out;
}
int main() {
Point p;
p.x = 8;
p.y = -2;
cout << p << endl;
return 0;
}Output is (8, -2). The compiler rewrites cout << p as operator<<(cout, p).
Add and print together
Once both operators exist, an expression looks like ordinary math followed by an ordinary print. That is the point of the feature: the call is still a function, but the line reads as the idea.
Example
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
Point operator+(Point other) {
Point sum;
sum.x = x + other.x;
sum.y = y + other.y;
return sum;
}
};
ostream& operator<<(ostream& out, Point p) {
out << "(" << p.x << ", " << p.y << ")";
return out;
}
int main() {
Point start;
start.x = 0;
start.y = 0;
Point step;
step.x = 3;
step.y = 4;
cout << start << endl;
cout << start + step << endl;
return 0;
}First line (0, 0). Second line (3, 4). start is still the origin.
Keep the meaning of the symbol
Overload an operator only when a reader already knows what it should do. + for a sum or a combined offset is fair. + that writes a file is not. The compiler will accept a surprising body. Other people will not.
| Operator | Typical use on your type |
|---|---|
+ | Combine two values; return a new one |
<< | Write a readable form to a stream |
== | True when both sides mean the same value |
You cannot invent a brand-new symbol. You can only teach an existing operator a new type. Precedence stays the same as for int: you do not get to make + bind tighter than *.
What to write next
Function overloading (same name, different parameters) and operator overloading (same symbol, your type) are cousins. Templates go further: one function body, many types, without listing each overload by hand. That is the next chapter.
StudyGrid compiles these programs as C++17. They do not touch the filesystem, so they run in/cpp/try the same way they would on Compiler Explorer.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C++ editor at /cpp/try. Change one measurement and check whether the result still has the right unit.
Physics
Add two force components
Forces in a plane add componentwise. (3, 4) + (1, 2) is (4, 6). Overloading + lets you write f + g instead of a helper named add_force. The geometry is still vector addition, not a trick of the language.
F = F₁ + F₂
Example
#include <iostream>
using namespace std;
struct Force {
double fx, fy;
Force operator+(const Force &o) const { return {fx + o.fx, fy + o.fy}; }
};
int main() {
Force a{3.0, 4.0}, b{1.0, 2.0};
Force c = a + b;
cout << c.fx << " " << c.fy << endl;
return 0;
}Maths
Add two 2D points
Translation is vector addition. (1, 2) + (3, 4) = (4, 6). The same + you use on ints, defined for a type that is two numbers glued together.
Example
#include <iostream>
using namespace std;
struct Point {
int x, y;
Point operator+(const Point &o) const { return {x + o.x, y + o.y}; }
};
int main() {
Point a{1, 2}, b{3, 4};
Point c = a + b;
cout << c.x << " " << c.y << endl;
return 0;
}