9.3 — Overloading the I/O operators

For classes that have multiple member variables, printing each of the individual variables on the screen can get tiresome fast. For example, consider the following class:

If you wanted to print an instance of this class to the screen, you’d have to do something like this:

Of course, it makes more sense to do this as a reusable function. And in previous examples, you’ve seen us create print() functions that work like this:

While this is much better, it still has some downsides. Because print() returns void, it can’t be called in the middle of an output statement. Instead, you have to do this:

It would be much easier if you could simply type:

and get the same result. No having to break up output across multiple statements, and no having to remember what you named the print function.

Fortunately, by overloading the << operator, you can!

Overloading operator<<

Overloading operator<< is similar to overloading operator+ (they are both binary operators), except that the parameter types are different.

Consider the expression std::cout << point. If the operator is <<, what are the operands? The left operand is the std::cout object, and the right operand is your Point class object. std::cout is actually an object of type std::ostream. Therefore, our overloaded function will look like this:

Implementation of operator<< for our Point class is fairly straightforward -- because C++ already knows how to output doubles using operator<<, and our members are all doubles, we can simply use operator<< to output the member variables of our Point. Here is the above Point class with the overloaded operator<<.

This is pretty straightforward -- note how similar our output line is to the line in the print() function we wrote previously. The most notable difference is that std::cout has become parameter out (which will be a reference to std::cout when the function is called).

The trickiest part here is the return type. With the arithmetic operators, we calculated and returned a single answer by value (because we were creating and returning a new result). However, if you try to return std::ostream by value, you’ll get a compiler error. This happens because std::ostream specifically disallows being copied.

In this case, we return the left hand parameter as a reference. This not only prevents a copy of std::ostream from being made, it also allows us to “chain” output commands together, such as std::cout << point << std::endl;

You might have initially thought that since operator<< doesn’t return a value to the caller that we should define the function as returning void. But consider what would happen if our operator<< returned void. When the compiler evaluates std::cout << point << std::endl;, due to the precedence/associativity rules, it evaluates this expression as (std::cout << point) << std::endl;. std::cout << point would call our void-returning overloaded operator<< function, which returns void. Then the partially evaluated expression becomes: void << std::endl;, which makes no sense!

By returning the out parameter as the return type instead, (std::cout << point) returns std::cout. Then our partially evaluated expression becomes: std::cout << std::endl;, which then gets evaluated itself!

Any time we want our overloaded binary operators to be chainable in such a manner, the left operand should be returned (by reference). Returning the left-hand parameter by reference is okay in this case -- since the left-hand parameter was passed in by the calling function, it must still exist when the called function returns. Therefore, we don’t have to worry about referencing something that will go out of scope and get destroyed when the operator returns.

Just to prove it works, consider the following example, which uses the Point class with the overloaded operator<< we wrote above:

This produces the following result:

Point(2, 3.5, 4) Point(6, 7.5, 8)

Overloading operator>>

It is also possible to overload the input operator. This is done in a manner analogous to overloading the output operator. The key thing you need to know is that std::cin is an object of type std::istream. Here’s our Point class with an overloaded operator>>:

Here’s a sample program using both the overloaded operator<< and operator>>:

Assuming the user enters 3.0 4.5 7.26 as input, the program produces the following result:

You entered: Point(3, 4.5, 7.26)


Overloading operator<< and operator>> make it extremely easy to output your class to screen and accept user input from the console.

Quiz time

Take the Fraction class we wrote in the previous quiz (listed below) and add an overloaded operator<< and operator>> to it.

The following program should compile:

And produce the result:

Enter fraction 1: 2/3
Enter fraction 2: 3/8
2/3 * 3/8 is 1/4

Here’s the Fraction class:

Show Solution

9.4 -- Overloading operators using member functions
9.2a -- Overloading operators using normal functions

153 comments to 9.3 — Overloading the I/O operators

  • Alexxx

    In the last code listing main function is redundant.

  • Angmar

    Hi, I ran into a peculiar situation and the error message is not making sense at all. In line 48, if I save a fraction from the the return value of the overloaded ‘*’ operator, cout seems to be okay with it. However I can’t directly use temporary value obtained from f1*f2. I wonder why is that?

    • Alex

      The problem here is that f3 is an l-value, and f1 * f2 is an r-value. You overloaded operator<&lt to take a Fraction by reference. References can only bind to l-values. If you update your overloaded operator to take const reference Fraction parameter, then it should work.

  • PierreLaCroix

    why is it not possible to do the operator overloading as oneliner like this:

    I tried that in my own code and for ostream it works as oneliner but for oFstream it doesnt:

    The error i get is:
    …..cpp:64: Fehler: invalid initialization of reference of type ‘std::ofstream& {aka std::basic_ofstream<char>&}’ from expression of type ‘std::basic_ostream<char>’
         return out << myObj.stringmember;

    btw this site is awesome!

    • Alex

      I’m not sure. I tried your Point example above in Visual Studio 2017 and it compiled and ran fine. Do you get the same error in the Point case?

      • PierreLaCroix

        Yes I do…

        Copied your point code, ran it & it worked and then added this:

        main.cpp:29: Fehler: invalid initialization of reference of type ‘std::ofstream& {aka std::basic_ofstream<char>&}’ from expression of type ‘std::basic_ostream<char>’
             return out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ")";

        Btw I use Qt Creator…

        • Alex

          Sorry, I missed the detail that you were trying to do this with ofstream, not ostream.

          The class hierarchy for file streams is: ios_base <- ios <- ostream <- ofstream std::cout is of type ostream. Therefore, when you try and override operator<< using ofstream (instead of ostream), when you pass in std::cout, it fails, because an ostream can't be converted to an ofstream (which is a superclass of ostream).

  • Tamara

    Hello, I’ve one question.
    I’ve stumbled on a piece of code which, when overloading the operators >> or <<, inside the function body, instead of the the reference in or out directly uses cin or cout. So, it looks like this:
    istream& operator>>(istream&in, const Point& point)
    cin>>"input something";
    return in;
    Or in the case of the operator << it looks like this:
    ostream& operator<<(ostream&in, const Point& point)
    cout<<"print something";
    return cout;

    My question is, does this work and how?

    • Alex

      If the caller was passing in cout for ostream (or cin for istream) anyway, then it’s essentially the same. But if the caller is passing in some other stream (like cerr instead of cout) then this code will still print to cout, which is incorrect.

  • Alan

    Hi, Alex, I got one thing to ask regarding to the quiz.

    Is it better to use in.ignore(32767, ‘/’) than declare a char variable to extract the ‘/’ separator? Also, we can put in.ignore(32767, ‘n’) right after in >> m_denominator. By “better” I mean to handle unwilling situations, but I am not sure whether this will reduce the efficiency or not.

    • Alex

      Yes, it’s better to use ignore than extract a single char, just in case there’s more than one char worth of additional input buffered. You shouldn’t call ignore unless you have a specific reason to. Otherwise it’s just extraneous.

Leave a Comment

Put C++ code inside [code][/code] tags to use the syntax highlighter