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. There would be no breaking 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

201 comments to 9.3 — Overloading the I/O operators

  • Han

    i am trying to overload << operator using normal function. However, get a non-static member of class error.
    could you help explaining this please?

  • Lan

    Hi Alex
    How to overload the I/O operators for cin.get in char or std::getline in string

  • Mren

    Hi Alex~  

    Why MUST the Fraction parameter for the ostream be const? My code didn't work when it wasn't const and I can't figure out why. Thanks!

    • Alex

      Because of this: << f1 * f2. This Fraction multiplication returns a Fraction by value which is an r-value. r-values can only bind to const references, not non-const references.

  • Mike

    Hi, may I drop a quick suggestion?

    Upon reading, I was initially confused as to the mechanics of the operator<< function. Naively, I would have expected the "printing", so to speak, to occur once the return statement had executed. This caused a little confusion as to why we weren't assigning anything to the out variable prior to returning it. It then dawned on me that the printing actually occurs within the function prior to the return statement, however I do believe a small sentence to clarify the point would not go amiss!

    Thank you for your wonderful content as ever 🙂

    • Silence

      Hi Mike.

      Let me shortly describe what is actually happening when operator<< is called. Since your call looks like

      you can notice, that operator<< takes two arguments -- left std::cout and right Fraction fr. So, you actually pass std::cout as out and fr as Fraction parameter. So when (in the operator body) you call

      , you do it for out as std::cout and, therefore, this is printed. So, the main aim of the operator is now done, but we want to chain such calls (less formally, we want (std::cout << fr) to return std::cout , so the (std::cout << fr) << "and so on" statement can be performed). In order to do so, we return out itself (recall that out is cout now).

  • Hello sir,

    In your code, you used -

    But, even if I don't use this (or reduce this), the program still works perfectly fine. I tried to give number of cases, but it passed every time.

    Are there any specific cases, for which the above code is required?

    Thanks,
    Ruturaj

  • Andi

    Just a minor correction to be consistent:  
    In the solution of the quiz in lines 34, 35, 58 and 64, the ampersand (&) of the in and out parameters should be next to the parameter name and not type:

  • Wesam

    Sorry if this is a basic question, but why is the function operator>> and << returning a reference? As in ostream& ... and istream&...

    Thanks.

    • Alex

      Because we don't want to make copies of the ostream and istream objects. We want to reference and use the ostream and istream objects that already exist.

      • Wesam

        Totally makes sense. I think that my true problem is not knowing enough about ostream and istream. I'm using your website in tandem with my college class, and my book is doing things in a wonky order.  

        I read a bit of chapter 18 to get a better grasp of streams, ostream and istream, but I still don't really understand how these classes work.

        Also, I truly appreciate the time you've put into this website. It's been an amazing help for me in my career as a student and hopefully in the future too. I've been following along for about 4 months now and it's been great.

  • Wurschter

    Why we have to return "std::istream&" when we overload the >> operator?
    For the << operator it is clear, when it comes to print more.
    But can we do something like:

    ?

  • Wurschter

    What is better, overloading the << operator, or writing a print function that returns std::string?

    • Hi Wurschter!

      In my opinion, a "toString" function, because you'll find yourself in need for a string representation of your object in situations where you don't want to or can't use the console. You can use @std::stringstream inside the @toString function if you want to keep the easy conversions that @std::basic_ostream offers.

  • Vamshi malreddy

    Hey can anyone help me with this

    In the quiz, for overloading operator<< I defined it as

    friend ostream& operator<<(ostream& out,Fraction &f1);

    not making the argument "const" Fraction &f1

    but this flags an error why??

    • Hi Vamshi!

      @operator<< is called with

      as an argument in @main.

      is an rvalue and cannot be modified. By removing the "const" you're saying that it can indeed be modified, which is wrong.

      • Vamshi malreddy

        But we are just saying that it can be be modified by not appending "Const" ....but we aren't right?? Did we ? So this should not flag any error!

        • You removed the "const" from @f1 in @operator<<, this means that @operator<< is allowed to modify the argument. You're trying to pass a non-modifiable argument (f1 * f2), so you get an error.

  • DecSco

    My operator<< function:

    Then it doesn't output e.g. "2/1" but rather "2".

    Is that the right place for that check or is there a better way?

  • Peter Baum

    typo: " No having to break up output across multiple statements, and no having to remember what you named the print function."

    1. "no" should be "not"
    2. The fragment is not a sentence; combine with previous statement.

  • Thomas Driscoll

    I built the program differently than the model, but it produces the same results. Thoughts?

    • nascardriver

      Hi Thomas!

      * Line 9: Unnecessary forward declaration.
      * Line 14, 15, 40, 55, 64-67, 78, 79, 103, 106, 111: Use uniform initialization.
      * Line 17: Use member initialization lists.
      * Line 34: This function shouldn't print "Fraction One" etc.
      * @simplifyResult: Bad variable names. Variable names should be descriptive.
      * Line 67: Declare this variable in the loop's header.
      * Line 68ff: Have a look at @std::min.

  • Rev

    Is it ok if i don't understand the explanation about return type? @@ I'm scared @@

  • 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]