Overloading the <<
Operator
Learn how to overload the <<
operator, so our custom types can stream information directly to the console using std::cout
So far, we've been using a simple terminal prompt to view the output of our program. Later in this course, we'll switch to rendering graphics using SDL.
But, there are some useful tricks we have that can help us make the most of our terminal for building out quick programs.
We've seen how we can stream numbers and strings to the terminal using the <<
operator:
std::cout << "Hello World " << 42;
We'd like to be able to do something similar with our custom objects. For example:
#include <iostream>
class Character {/*...*/}
int main() {
Character Enemy;
std::cout << Enemy;
}
To do this, we need to overload the <<
operator to be usable with std::cout
as the left operand, and Character
as the right operand.
Inspecting that in our IDE, or checking the documentation, would reveal it to be a std::ostream
, which is an output stream.
Because std::cout
is the left operand, the std::ostream
reference needs to be the first argument for our overload, whilst the Character&
is the second argument. So, our overload would look like this:
void operator<< (
std::ostream& Stream,
const Character& Enemy
) {
Stream << "Hello World";
}
With that, our code will now compile, with "Hello World" being streamed to the terminal:
#include <iostream>
class Character {/*...*/}
void operator<<(std::ostream&, const Character&)
int main() {
Character Enemy;
std::cout << Enemy;
}
Hello World
Chaining <<
Operators
We want to make two updates to make our overload more useful. Firstly, we want to be able to chain <<
operators, just like we can when streaming other types. For example, we want to be able to do things like this:
std::cout << MyCharacter << '\n';
To enable this, we need to ensure our overload function returns a reference to the stream we're writing to. So, we update the return type of our overload, and return
the stream that was used as the left operand:
std::ostream& operator<<(
std::ostream& Stream,
const Character& Enemy
) {
Stream << "Hello World";
return Stream;
}
With that, we can now chain additional <<
operators after streaming a Character
object:
#include <iostream>
class Character {/*...*/}
void operator<<(std::ostream&, const Character&)
int main() {
Character Enemy;
std::cout << Enemy << "!!!";
}
Hello World!!!
Serializing Objects
Secondly, we want our Character
output to generate some meaningful output, so we can stream it rather than "Hello World".
Representing the state of objects as a simple string is sometimes referred to as serialization. We'll see more of this in the next course, as it unlocks features like saving files to the user's hard drive and communicating over the internet, enabling programs like multiplayer games.
For now, we can just create a simple function that generates a string that stores some info about our object, in this format:
[Level] Name (Health / MaxHealth)
To accomplish this, we could add a public function that uses the std::format
techniques we covered in the previous lesson:
class Character {
public:
std::string Serialize() const {
return std::format(
"[Level {}] {} ({} / {})",
Level, Name, Health, MaxHealth);
}
private:
std::string Name { "Goblin Warrior" };
int Level { 15 };
int MaxHealth { 150 };
int Health { 90 };
};
Finally, let's update our overload to use this function:
std::ostream& operator<<(
std::ostream& Stream,
const Character& Enemy
) {
Stream << Enemy.Serialize();
return Stream;
}
With that, everything is in place. Developers can now easily stream information about our objects to std::cout
like any other type:
#include <iostream>
class Character {/*...*/}
void operator<<(std::ostream&, const Character&)
int main() {
Character Enemy;
std::cout << Enemy;
}
[Level 15] Goblin Warrior (90 / 150)
Summary
In this lesson, we explored how to overload the <<
operator in C++, enabling us to stream custom objects directly to the console using std::cout
. Through practical examples, we demonstrated chaining operators and serializing objects for more effective output. Key topics included:
- Understanding the concept and implementation of overloading the
<<
operator for user-defined types. - Learning how to make the overloaded operator compatible with
std::cout
by usingstd::ostream&
and a reference to our custom type as parameters. - Exploring the technique of chaining
<<
operators by returning a reference to the stream. - An introduction to object serialization, through implementing a
Serialize
method in our class.
System Calls and Terminal Management
This lesson introduces system calls, and how to use them to clear the terminal