Overloading the << Operator

Learn how to overload the << operator, so our custom types can stream information directly to the console using std::cout
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
3D Character Concept Art
Ryan McCombe
Ryan McCombe
Updated

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.

Overloading the << Operator

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

Overloading << as a Member Function

It’s tempting to define the << overload as a member function on the Character class, to keep everything encapsulated. However, as a member function, it would need to be defined by the type of the left operand. In this case, that is the std::ostream class, not our Character class.

The std::ostream class is not our code, so extending it is quite difficult. As such, we define overloads as free functions instead. In a large project, these functions can be defined within the same files we use for the right operand’s type - eg Character.h - so our code is still kept organized.

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 using std::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.

Preview of the Next Lesson

So far, our programs have been restricted to outputting a vertical stream of text. In the next lesson, we’ll begin to introduce the concept of an application loop. This involves our program clearing the screen and regenerating new output on each iteration, which is the basis for much richer interactions.

Was this lesson useful?

Next Lesson

System Calls and Terminal Management

This lesson introduces system calls, and how to use them to clear the terminal
3D art showing a maid cleaning
Ryan McCombe
Ryan McCombe
Updated
Lesson Contents

Overloading the << Operator

Learn how to overload the << operator, so our custom types can stream information directly to the console using std::cout

3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, Unlimited Access
Odds and Ends
3D art showing a progammer setting up a development environment
This lesson is part of the course:

Intro to C++ Programming

Become a software engineer with C++. Starting from the basics, we guide you step by step along the way

Free, unlimited access

This course includes:

  • 60 Lessons
  • Over 200 Quiz Questions
  • 95% Positive Reviews
  • Regularly Updated
  • Help and FAQ
Next Lesson

System Calls and Terminal Management

This lesson introduces system calls, and how to use them to clear the terminal
3D art showing a maid cleaning
Contact|Privacy Policy|Terms of Use
Copyright © 2024 - All Rights Reserved