Search

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)

Conclusion

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
Index
9.2a -- Overloading operators using normal functions

165 comments to 9.3 — Overloading the I/O operators

  • Cumhur

    To understand better the concept of const ref i made an experiment:

    For the x reference;
    when i take x as a copied value (Fraction x) program works.
    However when i take x as a non constant reference compiler gives eror. Error is in line: (//std::cout << f3 * f1;) while the line (std::cout << f4;) works without eror. Couldnt figure out why.

    Figured out: * operator requires constant Fraction, so compile gives an eror.

    • nascardriver

      Hi Cumhur!

  • Matt

    My initial attempt was to write the overloaded operator<< and operator>> like this:

    in Fraction.h inside class Fraction

    in Fraction_Friend_Functions.cpp

    However, applying the const keyword to the std::ostream and std::istream reference parameters and return references produced errors.

    Logically, since I know my overloaded operator functions are not going to change the ostream or istream objects, it made sense to make those items const.  I'm not completely sure why I cannot do that.  Any ideas?

    Note: removing the const keywords related to ostream and istream allows the code to compile fine.

    Thanks,
    Matt

    • nascardriver

      Hi Matt!

      @std::ostream::operator<< and @std::istream::operator>> are non-const, because they modify the streams internal buffer. If you make @out and @in const you're not allowed to modify the objects, but you're trying to do so by using their operators.

  • Matt

    The overloaded operator>> confuses me:

    Does >> take as much input as it can until it hits something that is not of the variable type it is inputting into?  For example, I am confused how it knows the difference between user input of 1/2 (single digit num/denom) vs. 1111/22222 (multi-digit num/denom).

    Testing the code it seems to work for any user input of format [integer][single char][integer], so it seems my hypothesis is correct...

    • nascardriver

      Hi Matt!

      > how it knows the difference between user input of 1/2 (single digit num/denom) vs. 1111/22222 (multi-digit num/denom)
      You told std::cin to extract into a @Fraction, so your operator gets called. Your operator tries to extract an int (Line 6), @std::istream knows how to do that, doesn't matter if single or multi digit, same with a char.

  • Benjamin

    And hi again,

    I am playing around with classes and overloading operators. Within 10 minutes I encountered like 2 things, I do not know how to solve or how to solve them best. As they span different lessons, I decided to put them here, which is as far as i read until now:

    I wrote a little class to represent complex numbers. As of now, I overloaded the "+" and "<<" operator. Each of which works fine. The following code compiles without any problem:

    Now the problems:

    1) It seems I cannot simplify my code as

    It throws the error "cannot bind 'std::ostream {aka std::basic_ostream<char>}' lvalue to 'std::basic_ostream<char>&&'"

    Why is that?

    2) If I want to add one of my Complex numbers to an anonymous object like

    I can't use the overloaded operator "+" as I wrote it, because it accepts a reference to a Complex-object. I understand that. How can I tell the compiler to use a reference, if possible, and the value, if a reference cannot be provided? I first thought I can use different prototypes for "operator+", but this leads to an ambiguity (How is the compiler supposed to know, whether to copy the value or take the reference, if I pass a variable?). Overloading "+" with just one function that accepts values seems unsatisifying to me. I like passing by reference 🙂

    • nascardriver

      Hi again Benjamin!
      I fixed your code by adding the const keyword (Lesson 8.10) wherever no modifications to the objects are made.

      Unfortunately I cannot tell you why this fixed it, so you'll have to wait for Alex or someone else to give you an explanation.

    • Alex

      Nascardriver hit upon the correct solution, even if he didn't understand why.

      The short answer is this:

      Parameter number is a non-const reference. Non-const references can only bind to l-values (not r-values). So this works when you pass in r (which is an l-value), but not y + z (which is an r-value temporary).

      To fix this, just make number a const reference. Const references can bind to both l-values and r-values, thus solving your issue.

      The same goes for your operator+ -- Complex{3,4} is an r-value, and your operator+ is taking non-const reference parameters. Making the parameters const will allow you to add both l-values and r-values.

      Const is really important to use appropriately with references, so make sure you understand this before proceeding. 🙂

      • Benjamin

        Thanks for both of your answers. Reading, re-reading and also re-re-reading the lessons plus solving the quizes and re-solving the quizes and also re-re- ... well no, i better stop here now. Point is, the more I read, the more I grasp the concepts.

        Given the following summary, did I get the core of it right?

        1. Chapter 1.3:

        - "An r-value refers to values that are not associated with a persistent memory address."

        => references can not refer to an address that doesn't exist, explaining why passing in an expression and/or an anonymous object, as I tried, didn't work. But:

        2. Chapter 6.11a:

        - "However, when a reference to a const value is initialized with an r-value, the lifetime of the r-value is extended to match the lifetime of the reference."
        - "References to const values are particularly useful as function parameters because of their versatility. A const reference parameter allows you to pass in [...] a literal, or the result of an expression:"

        => const references are capable of taking literals, expressions etc. In that case the corresponding r-value's lifetime is expanded to the lifetime of the const reference.

        Therefore, the rule that you mentioned a 1000 times: const everything that doesn't need to be non-const for a reason.

  • Luhan

    Could you explain a little bit further about how cout is an object which get all the information with the operator <<, and do the output?

    • Alex

      std::cout is an object that is provided by the C++ standard to do console output. std::cout knows how to output fundamental types (e.g. int, float) and C-style strings. How exactly it does that, I don't know.

      But for our custom classes, we can override operator<< and then leverage the things std::cout already knows how to do to make it output what we want our class to output.

  • 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

    Hello,
    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:

        Still:
        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 all code inside code tags: [code]your code here[/code]