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

270 comments to 9.3 — Overloading the I/O operators

  • chai

    Hi, is it necessary for this operator overload to return istream in this example? It seems to work fine if it was implemented as void.

    Thanks

  • use of 'reduce'

    Why do we need to reduce f1 again? The only situation where we need a fraction to be reduced is multiplying them which we already call 'reduce' in 'operator*', why should we call 'reduce' again in operator>>?

    • nascardriver

      You can always try to reduce fractions
      1/4 + 1/4 = 2/4 can be reduced to 1/2
      No multiplication here.

      The user can input anything, so we can try to reduce it. If they input
      4/16
      we can reduce it to 1/4

  • Mehta

    >>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.
    shouldn't 'that'  be replaced with 'then'?

    Why can't we have the following and get 'stack overflow'?

    • nascardriver

      I removed the second "that" from the sentence, thanks!
      I don't understand why you're expecting the code to produce a stack overflow. There's nothing wrong with that code.

  • Sonia

    1) The reason why we chained output in operator<< is that 'std::cout' is not going to change but here in the example below, every time we use operator+ for MinMax on line #60, the new different output we get, is my understanding correct? Also can we use reference return type for arithmetic operator if we want to have something like " point.m_x = point._m_x+point.m_y"?

    2) Also, you mentioned "Any time we want our overloaded binary operators to be chainable in such a manner, the left operand should be returned (by reference).", then why the arithmetic operator which were chained didn't return by reference?Like examples below?

    ****************example #1 **********

    ****************example #2 **********

    • nascardriver

      I didn't understand (1). For (2), the arithmetic operators can't return references, because they are creating a new object. You'd be returning a reference to a local temporary, ie. a dangling reference.

  • Henry

    Why does my Fraction.h file not complain about not knowing what std::ostream is? (since I haven't #included the <iostream> header)

    • nascardriver

      Header's aren't compiled directly. They only get compiled as part of every source file that gets compiled. You're probably including "Fraction.h" after already having included <iostream>.
      "Fraction.h" should include <iostream>.

      • Henry

        Ohh, that makes a lot of sense! Is it still good practice to #include standard library files that you use in your header files so you know for certain that when the compiler reaches the line, it will know what you are referencing?

        Thanks!

        • nascardriver

          Yes. Every header should be compilable on its own, ie.

          main.cpp

          This should compile and link. Your header should not depend on any external includes.

  • Luxary

    Is this properly sanitized user input? The half-baked check was bugging me:

    • nascardriver

      I don't see a way how to break your function, so I'd say it's properly sanitized.

      Say line 12 fails, you'll still run line 14, 16, 18, 20, and 5, which all fail, before you finally clear the stream. To get around this, you could write a `getInteger` function that performs the extraction and clears the stream if necessary, repeating that until the input is valid. Then your `operator>>` doesn't need to loop anymore and only has to call `getInteger`, `in.ignore`, and `reduce`.

  • Mo

    When you use this code:

    You are not creating an object of Fraction class before returning. So is this casting happening implicitly? What's the name for this, implicit casting or something else?

    • nascardriver

      There is no cast. The curly braces create an object of the return type of the functions. The code is the same as

  • Gabe

    I have a question regarding operator<<

    I'm reading more about operator overloading using a C++ book I own (Starting out with C++ Early objects), and the author mentions that: "The second parameter should be passed by value. The first parameter should be passed by reference because ostream parameters should never be passed by value."

    Here is the example:

    So, because we are just printing the contacts of the object, shouldn't it be passed by const reference? It doesn't say why. 'a' has an overhead and for efficiency reasons, you want to pass it by reference in addition to not modify it so pass it by const.

    • nascardriver

      As a general statement, the quote from your book is wrong. If the second parameters is a non-fundamental type and not lightweight (eg. `std::string_view`), it should be passed by const reference.
      Without knowing the definition of `Length`, I suppose its only member is a single fundamental type (A number). If that's the case, then the cost of passing `Length` by value is the same as passing the held type by value, ie. it's faster than passing it by reference. If the quote is only referring to this example, it's correct.

  • Gabe

    Quiz:

  • Mohammad

    Why doesn't the following code run ?

    The compiler flags an error at the line:

    So what should I change?
    Sorry to disturb you but advanced thanks.

    • nascardriver

      You don't have a default constructor. Add a default constructor.
      When you get an error, always post the error message.

  • Eric

    On the point printing programs the point x = 2.0 was entered (as a double), but 2 was printed. If I wanted the 2.0 printed, what would be the best method to go about it?

    • Alex

      Use setprecision(), discussed in lesson 4.8.

      • Eric

        I had forgotten 'setprecision()' so thank you for reminding me.  
        However, it is not in its initial application achieving the effect that I wanted.
        The program below still produces d1 = 1.
        My consideration was for significant figures.
        1, 1.0 and 1.00 have different meanings when considering significant figures.  

        This outputs:  "1"

        • nascardriver

          You can add `std::fixed` to print trailing zeroes

          This change the output to "1.00000".

  • kavin

    Hi, for the quiz if you add,

    in line 79, it reduces even the inputs in the displayed out. 500/200 * 644/644 is printed as "5/2 * 1/1 is 5/2"  

    But we want the actual input "500/200 * 644/644" to be displayed as it is and need only the result as the simplified fraction in the output right ?

    • Alex

      The quiz is ill-specified in this regard, as it doesn't say whether the fractions displayed should or should not be reduced.

      If this behavior is desired, this would be one good reason to make the reduce function explicitly callable rather than have every Fraction auto-reduce.

  • Oliver

    Hi,
    Would it be logical to make the operator overloaded function for << (output stream) a CONST function, as it is virtually the same as a 'void print()' function which we have been dubbing with 'void print() const'?
    For example, is it wrong to write,
    friend std::ostream& operator<<(std::ostream& out, const Fraction f) const;
    ?

    Thanks

  • Ryan

    Can you make the function "friend std::ostream& operator<<(std::ostream &out, const Fraction &f1)" a static.

    Since the operator<< function above does not use the hidden this pointer. However static are member functions but friend functions are non-member functions, so would it be possible? If so, how could it be implemented. Just putting static infront of the word friend?

    • nascardriver

      `static` combined with `friend` doesn't make sense. `static` members already have access to private data.
      `operator<<` can't be a (`static`) member. When you use `<<`, the operator is searched for in the global namespace and the namespace of the argument, but not inside of classes.

  • Samira Ferdi

    Hi, Alex and Nascardriver!

    Why frac is const Fraction?

    If, I remove the const, I have compile errors.

    • Alex

      Mainly because it's a good idea to pass objects by const reference instead of by reference to ensure the function doesn't inadvertently modify the argument.

      But the reason it fails in this specific case is because f1 * f2 is an r-value, and r-values can only bind to const references.

  • Kevin

    Hi.  I am working on an assignment for a class, and we are being asked to make a dog class with the specifications below.  I have tried to compile this in many different ways but I keep getting one error that says that the operator<< function must only take one argument, which I don't understand since it obviously requires 2 (the out stream object and the user defined object).  I've included my class definition and the operator<< function implementation, as well as the error message.  Please help!

    In file included from main.cpp:4:0:
    Dog.h:82:63: error: 'std::ostream& Dog::operator<<(std::ostream&, const Dog&)' must take exactly one argument
    std::ostream& Dog::operator<< (std::ostream& s, const Dog& rhs)

    • Kevin

      Nevermind, I figured it out.  I had to declare the operator<< function outside of the class, then declare it as friend inside the class.  THEN (the important part), don't include the Dog:: part in the function implementation, because the operator<< function isn't part of the Dog class.  Like so:

      • nascardriver

        Exactly. If you declare it as a member function, you're overloading the shift operator with you class implicitly on the left.

  • hellmet

    Teehehehe

    I think this would be a fun quiz question!

  • Quiz solution: line 66. Unused variable `char c`.
    When I was figuring out how to get rid of the slash `/` from `2/3`, I know it should be done with `in.ignore`, but because I was unsure, I also use a temporal `char`.

    Idk why but I found this funny XD

  • Taksh

    Hey Alex,

    Thank you for this great website. I truly appreciate your efforts.

    So, I followed your instructions. But I keep getting an error when I try to print a variable:

    Invalid operands to binary expression.

    What do you think might be causing this?

  • Jeremiah

    In the answer to your quiz, you ignore the '/' character by inserting it into a unused character "char c" . See your code below:

    However, you can avoid creating this unnecessary variable if you use the ignore function built into the istream object:

    This seems more intuitive to me. Is there any reason to use the former over latter?

    • Alex

      Only if you want to examine the separator after extracting it. In this case, we don't do that, so your solution is better. I've updated the lesson. Thanks!

  • Barne

    "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;"

    This instantly makes sense thanks to the previous "this pointer" lesson!

    I just wish we learned a little more about how std::cout works at this point. We've been using it since "Hello World!", and it's just this mystery object we feed stuff into and it gets output to a terminal somehow.

  • Eru

    Hello, it was stated that;

    "Any time we want our overloaded binary operators to be chainable in such a manner, the left operand should be returned (by reference)."

    However in one of the previous chapters, we've chained the operator+ for MinMax without returning by reference.
    Does the below code(line 56) for MinMax not considered as chaining or am i missing something here?

    Thanks beforehand :)

    • Alex

      Yeah, the "in such a manner" is actually an important qualifier here, as I'm not trying to define an absolute rule, but rather one specific to this case.

      Basically, if the overloaded operator returns a temporary object, you need to return the temporary object by value, as returning it by address or reference would result in a dangling pointer/reference when the temporary object went out of scope. This is typically the case in operators overloaded as friend functions.

      In the case where the implicit object is being modified, you can return the implicit object itself by reference. This is typically the case in operators overloaded as member functions.

Leave a Comment

Put all code inside code tags: [code]your code here[/code]