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

3D art showing a maid character
Ryan McCombe
Ryan McCombe
Posted

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>
using namespace std;

class Character {
    string Name { "Goblin Warrior" };
    int Level { 15 };
    int MaxHealth { 150 };
    int Health { 90 };
};

int main() {
    Character MyCharacter;
    std::cout << MyCharacter;
}

To do this, we need to overload the << operator. Because we're calling << on the left operand, we need to overload it on the object type that is returned from std::cout.

Inspecting that in our IDE, or checking the documentation, would reveal it to be a std::ostream.

So, our overload would look like this:

void operator<< (
  std::ostream& Stream,
  Character& Character
) { 
  Stream << "Hello World";
}

Now, attempting to stream out a character will result in "Hello World" being streamed to the terminal.

Character MyCharacter;
std::cout << MyCharacter;
Hello World

We want to make two changes here:

Firstly, we want to be able to chain << operators. We'd like to do things like this:

std::cout << MyCharacter <<  std::endl;

To enable this, we need to make sure our function returns a reference to the stream we're writing to, so we can stream additional things to it. So, we update the return type of our function, and return the stream:

std::ostream& operator<< (
  std::ostream& Stream,
  Character& Character
) { 
  Stream << "Hello World";
  return Stream;
}

With that, our code works:

Character MyCharacter;
std::cout << MyCharacter << "!!!";
Hello World!!!

Secondly, we want to stream out something that is appropriate to our character, rather than "Hello World".

A useful way of doing this is by adding a public method to our class. Lets imagine we wanted to log out this:

[Level 15] Goblin Warrior (90 / 150)

To accomplish this, our new Character.Write() method might look something like this:

class Character {
public:
  void Write(std::ostream& Stream) {
    Stream << "[Level " << Level << "] ";
    Stream << Name;
    Stream << " (" << Health << " / " << MaxHealth << ")";
  }
private:
  std::string Name { "Goblin Warrior" };
  int Level { 15 };
  int MaxHealth { 150 };
  int Health { 90 };
};

Finally, lets update our overload to use this function:

std::ostream& operator<< (
  std::ostream& Stream,
  Character& Character
) { 
  Character.Write(Stream);
  return Stream;
}

Now, info about our characters can be streamed directly to our terminal:

std::cout << PlayerCharacter <<  std::endl;
std::cout << EnemyCharacter <<  std::endl;

Clearing the Terminal

Soon, we'll be working with libraries that help us create fully interactive user interfaces. But, before we move on to that, it's worth quickly covering how we can improve the terminal output.

Even though the products we ship to users aren't going to be interacted with on the terminal, Command Line Interface (CLI) is very common for internal tools, intended to be used by other developers.

Currently, our output has always been a vertical stream of text. Typically, a better user experience is update the existing output on the screen, rather than appending new information below the old.

A common technique to accomplish this is to clear the screen and redraw the new output.

There are several ways to clear a terminal, and it varies from operating system to operating system. Below is a simple example that works on Windows, Linux and Apple devices:

void ClearScreen() {
#if defined _WIN32
  system("cls");
#elif
  system("clear");
#endif
}

We can use this to create a simple clock application, that updates every second:

#include <chrono>
#include <iomanip>
#include <iostream>
#include <thread>

using namespace std;
using namespace std::chrono;

void ClearScreen() {
#if defined _WIN32
  system("cls");
#elif
  system("clear");
#endif
}

void PrintTime(const time_t& Time) {
  tm TimeContainer;
  localtime_r(&Time, &TimeContainer);

  cout << put_time(&TimeContainer, "%r") << endl;
}

int main() {
  while (true) {
    time_point Time{system_clock::now()};
    ClearScreen();
    PrintTime(system_clock::to_time_t(Time));

    this_thread::sleep_for(seconds(1));
  }
}

Flickering and Double Buffers

Running this application, you may notice a small flicker where the screen is empty between updates. This would be especially noticable if we updated the screen more quickly, to simulate real time graphics.

The issue is caused simply by the time between clearing the screen, and drawing the new output. For a fraction of a second, there is nothing on the screen.

A common solution to this is a technique called double buffering. This involes two canvases, or buffers - the "front" buffer that is being shown to the user, and a "back" buffer where the next frame is being created.

Using this technique, the user only ever sees complete frames - the front buffer is shown until the back buffer is ready, then they get swapped out.

We cover this and other techniques for improving our visual output later in the course.

Next, we'll see how we can extend our use of the terminal to get input from our users, letting our software react to their input!

Was this lesson useful?

Ryan McCombe
Ryan McCombe
Posted
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

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:

  • 66 Lessons
  • Over 200 Quiz Questions
  • Capstone Project
  • Regularly Updated
  • Help and FAQ
Next Lesson

User Input in the Terminal

Learn how to get user input from the terminal, allowing our program to respond to use requests
3D art showing a character in a fantasy environment
Contact|Privacy Policy|Terms of Use
Copyright © 2023 - All Rights Reserved