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

163 comments to 9.3 — Overloading the I/O operators

  • Nur

    Hello Alex,
    I hope you are doing well! I have a question regarding to chainable objects. I have edited above your shown program little  bit. My opinion  is that there is no necessity of making chainable objects as you see (std::cout<<point1). You put the return by reference as to make chainable objects. As you see I made return by Value, since I don't prefer the objects works as chainable.I have just copied the of parameter "out", not get the original value of "out".I don't need the original value, since I don't want std::cout object to print on screen something else after std::cout<<point1; in main.

    #include <iostream>

    class Point
    {
    private:
        double m_x, m_y, m_z;

    public:
        Point(double x=0.0, double y=0.0, double z=0.0): m_x(x), m_y(y), m_z(z)
        {
        }

        friend std::ostream operator<< (std::ostream &out, const Point &point);
    };

    std::ostream operator<< (std::ostream &out, const Point &point)
    {
      
        out << "Point(" << point.m_x << ", " << point.m_y << ", " << point.m_z << ")";

        return out;
    }

    int main()
    {
        Point point1(2.0, 3.0, 4.0);
        std::cout << point1;

        return 0;
    }
    In case it is chainable, we must use the return by reference, I agree 100% with you. In above case, the copy of out returned and does nothing, so I just copied.Please, could you reply me why does this program give compiler error because of return by Value?
    It works like anonymous objects, anonymous objects can't be returned by reference.But It is not. What is the problem?
    Thanks in advance!

    • Alex

      std::ostream has deleted the copy constructor, so you can't pass or return it by value. This was done so that you don't try to make copies of std::ostream.

  • Michiel

    Everytime I try to overload the I/O operators my program crashes. I am working on a BigInt class and my output operator crashed again. Here is the code:

  • Riyaz

    why is this throwing an error at line 50?

    • Alex

      Because your friend declaration is this:

      But your implementation is this:

  • C++ Learner

    Hi, can you explain why there is a need to input char in this code?

  • Sihoo

    Alex,

    Even if I do not place the 'reduce()' in the istream operator overloading function, I still get the reduced form '1/4'. Is it because f1 * f2 return the anonymous Fraction object, which in turn results in the Fraction constructor to be called, hence reduce() is called?

    Tried to debug this to see the flow of the calls and seems like it does, but can you let me know if I'm correct on this? Thanks!

    • Alex

      This function creates an anonymous Fraction from the two Fractions being multiplied. This uses the Fraction constructor, which calls reduce(). When the Fraction is passed back to the caller (by value) a copy is made, but that's done using the copy constructor, not the (int, int) constructor.

  • Moj

    Alex, thanks once again for your precious outstanding tutorials! and I have a question:
    What if my class has three data members, and I need to detect the user data type to pour that input into the suitable member of my class? I know this is me (the programmer) who has to decide about the behavior of the overloaded operator, but I think it might be sometimes needed... (?)

    • Alex

      I'm not quite sure I understand what you mean by "detect the user data type to pour that input into the suitable member of my class". Are you thinking you'd have a class that defines an int, double, and string, and then your class could handle whatever kind of input the user gave you? The not-great way to do that would be to have separate constructors for each input type. A better way would be to use templates, which we cover in a later chapter.

  • Omri

    Regarding:
    ...std::cout is actually an object of type std::ostream...
    It seams resonable to expect that the following statements in main() should work:
    ...
    std::ostream c; //create a local std::ostream object named c
    c<<"abcd"; //use it to print a string to the screen
    ...
    Actualy, these statements do not compile.
    Why is that?

  • Miko

    Hi!

    Is it okay to put input checking inside operator overload function and also some output stuff to give information like in code below. Or should it be more simple and do invalid input cheking somewhere else?

    • Alex

      Personally I like to keep the overloaded operators as close to the original operators as possible. The built-in operators don't do any kind of validation checking or printing, so I wouldn't have mine do those things either. I'd have the caller do those things.

  • Rohit

    Hi Alex!
    Whats wrong with this code? I am getting output as 0/60 but i want 1/1?

    #include<iostream>
    using namespace std;
    class fraction {
      private:
        int m_num = 0;
        int m_den = 1;
      public:
        fraction (int num = 0 , int den = 1) : m_num(num) , m_den(den)
        {
          reduce();
        }
        static int gcd(int a , int b) {
          return b == 0 ? a : gcd(b,a%b);
        }
        void reduce() {
          int gcd = fraction::gcd(m_num , m_den);
          m_num /= gcd;
          m_num /= gcd;
        }
        void print() {
          cout<<m_num<<"/"<<m_den;
        }
        friend fraction operator* (const fraction &f1 , const fraction &f2);
        friend fraction operator* (int value , const fraction &f1);
        friend fraction operator* (const fraction &f1 , int value );
        friend ostream& operator<< (ostream& out , const fraction &f1 );
        friend istream& operator>> (istream& in , fraction &f1);
    };
    fraction operator* (const fraction &f1 , const fraction &f2) {
      return fraction(f1.m_num * f2.m_num , f1.m_den * f2.m_den);
    }
    fraction operator* (int value , const fraction &f1) {
      return fraction(value*f1.m_num , f1.m_den);
    }
    fraction operator* (const fraction &f1 , int value) {
      return fraction(value*f1.m_num , f1.m_den);
    }
    ostream& operator<< (ostream& out , const fraction &f1) {
      out<<f1.m_num<<"/"<<f1.m_den<<"\n";
      return out;
    }
    istream& operator>> (istream& in , fraction &f1) {
      char temp;
      in>>f1.m_num;
      in>>temp;
      in>>f1.m_den;
      return in;
    }
    int main () {
      cout<<fraction(6,10)*fraction(10,6);
      return 0;
      
    }

  • Christopher

    Hello! I'm a little confused on how returning the in/out (references to std::istream or std::ostream) returns what we put in the function that overloaded the input/output operators. How exactly does that work?

    • Alex

      First off, your overloaded I/O operators actually don't need to return in or out respectively. We do so solely so we can chain multiple calls to the overloaded operator together, which makes things a lot more convenient to use.

      The heavy lifting all happens within the overloaded operator body, using operator<< or operator>>, and that doesn't work any differently than if we'd called it outside of the function (in the body of main(), for example).

      I'm not sure I actually answered your question.

  • Matt

    FYI for the quiz GCC 6.2.0 with -std=c++11 throws errors without providing a default constructor. You may want to add that to your suggested solution.

    • Alex

      The Fraction class does have a default constructor:

      What am I missing? 🙂

      • Jeffry

        I think the problem Matt might have had is that in lesson 9.2 the default constructor is different.

        9.2 code  (The numerator isn't initialized.)

        9.3 code  (here the numerator is initialized)

        However, I came across another question.   For me the reduce() function no longer works, because we are calling it in the Constructor but in this example in main Fraction f1 we don't yet have the numerator or denominator to reduce.  

        However, if I added the following in the operator overload it works.

        Is there a better way to do it that I'm missing?

        • Alex

          1) I've updated the Fraction constructor in lesson 9.2 to be a default constructor, just for consistency.
          2) Your suggestion is entirely appropriate. I'll update the code to reflect this. Thanks!

  • Zachary Fojtasek

    On the very last line of my code, I am getting an "invalid operands to binary operator" error, and I can't figure out why it's happening. here is my code:

  • Aayush

    "" Because print() is a normal member function, it can’t be called in the middle of an output statement ""
    Why is it so? Although we have called the get function as:
    "" std::cout << "Point(" << point.getX() << ", " <<
        point.getY() << ", " <<
        point.getZ() << ")"; "".

    Why are we able to call the get func but not the print func?

    • Alex

      Poor wording on my part. I updated the wording to "Because print() returns void..."

      Function print() doesn't return anything, so the return value can't be sent to std::cout. However, the get functions return a value that can be sent to std::cout, so those are usable inside a print statement.

  • Best yet. Helped a lot. Thanks 🙂

  • Alex

    Hi, I used "in.ignore();" in the quiz instead when ignoring the "/" separator since it discards by default 1 character.

  • Rob G.

    I'm typing an input for e.g.: "4/6"

  • Rob G.

    Hi Alex, built the function but don't know why it works.

    I have 3 in statements that somehow know how to pick apart the numerator, the discard char x "/" and the denominator. I've confirmed each numerator and denominator are getting their correct numbers; both without the discard character. Whats going on here?

    • Alex

      If you're typing in "1.0 2.0" for example, the discard character would be the space. If you're typing "1.0/2.0" the discard character would be the '/'. But I'm just guessing -- what are you actually using as inputs?

  • Rob G.

    Do I leave the constructor fields blank below on initialization? The operator will input those.

    • Alex

      You can if that's what you desire.

      In this case, when point1 is created (with no parameters) it will use the default constructor, which will set m_x, m_y, and m_z to 0.0.

      Then you override those values with the user inputs.

  • Alexey Kolmyk

    Yes, I mean such kind of error and validation as you showed 🙂
    Thank you for your answer.

  • Alexey Kolmyk

    Hello Alex! I have one question about overloading operator>>. Should we add some validation for user input? For example, for your class Point - add string check. Is it common practice to do validation in this case?

    • Alex

      It depends on what you mean by validation. Let's take a sample use case of a failed input for the Point class.

      Consider the case where the user inputs: 3.0 q 5.0. 3.0 reads into m_x fine, but q is an invalid input. At this point, you can take any number of approaches:
      * Let std::cin fail the rest of the input and let the caller deal with it
      * Ask the user to try again
      * Throw an exception

      You're also now in the case where m_x has been updated but m_y and m_z haven't, which isn't great.

      Personally, I don't like having my operator>> do any kind of notification or re-prompting the user -- it's an input operator, not an output operator. I think if I wanted to do validation of any kind, most likely I'd read the input into temp variables, update the class members only if all the input is valid, and otherwise use the std::cin fail states to communicate back to the caller that the input operation failed -- and leave it up to them to determine what to do about that. That way your Point isn't left in an inconsistent state (half updated) and you're not making any assumptions about what the caller wants to do about failed inputs.

  • Matt

    I had to backtrack a couple of sections in order to see why you decided to make gcd() a static function. I found that the reason for doing so was commented previously in the code, but not in the code in this section.

    Also, I had another question about this fraction class. I was wondering why you chose to give the member variables initial values, while also giving them default arguments in the constructor. Wouldn't the default arguments have sufficed, or am I forgetting something?

    • Alex

      I added the comment to gcd() in this lesson so other users won't have to backtrack. Thanks for noting that.

      The default arguments in the constructor suffice just fine. I've updated the example to remove the initializers.

  • Santosh Srinivas

    Your C++ tutorials are Wonderful. Thank you so much for your effort.
    I have read more than 3 books but couldn't quite fully grasp the concept. But, your tutorials made it all a breeze with crystal clear and  simple explanations.
    Now the learning became fun.
    Thank you again.

  • ash

    you said returning by reference allows us to chain. Can you explain or simulate what happens if we return by value the ostream object?

    ostream operator<< (ostream &out, Point &cPoint);

  • Danielo

    I was doing the fractions exercise, and it took me quite a lot of time to figure out why my program wasn't working.

    It was because I wrote

    instead of

    I'm not sure why that const is important, I didn't use const when I defined the operator (because I did some changes to the objects) and it works fine.

    It doesn't seem logical to me why the object there has to be a const

    • Danielo

      oh, I just realized using const in the operator's parameters like

      lets me chain objects, while doing the same without the const doesn't let me.
      So, the consts are important. I still don't get why

      • Alex

        Same reason. Your return value is being returned by value. If you try and chain it, it's considered an anonymous object, and you can't pass anonymous objects to non-const references.

    • Alex

      f1 * f2 is an anonymous object. You can't pass anonymous objects to non-const references.

  • Kattencrack Kledge

    In the quiz, on these codes:

    Is it good practice to write it like this:

    • Alex

      I've never seen anybody do it that way, though it seems like it would work.

      Personally, I like to keep my return statements as simple as possible, so there's no ambiguity over what will be returned. In my version, it's clear that operator<< returns out. In your version, it's not as obvious.

  • Anddo

    OK this took time to process. Simply questions, I read almost all the answers which you explained well why the return by reference to the "out" object on the operator<< function. But what if I wanted to make it return by value !?. Once I do this

    VS 2015 gives me error on "return out;" being deleted function. I understand why we "should" work on this by passing by reference but I want to understand why it wont compile. The ostream is passed by reference so returning it by value shouldn't be a problem, it's not anonymous variable, is it ?

    • Anddo

      I searched a lot for this and made no sense to me until I found the answer. Returning by value is making a copy. Which leads the usage of copy constructor which is coming in the lesson "9.11 The copy constructor". Copy constructor of the all stream classes in C++ is disabled by having them made private.

      If I understood this incorrectly, please correct me.

    • Alex

      When you return a class by value, the compiler looks to see if there is a copy constructor to make the copy. It sounds like std::ostream has specified the copy constructor as "deleted" so that it can not be used in this way.

      We talk more about copy constructors at the end of this chapter.

  • Pip

    Alex, a minor detail in the quiz question of this section - a default parameter value appears to be missing in the constructor of the code supplied for the question (line 10):

    Compared to that given in the solution:

    I mention this because the former wouldn't compile for me (but perhaps you included this as test of your readers).

  • Connor

    Hello again Alex.
    In the quiz why do we define, in reduce():

    I understand that gcd() is a static function but since reduce is being defined already within the class why can't we drop the Fraction::

    It doesn't throw an error - should it? Is it good practice to always define static members as such?

    • Alex

      will throw an error on some compilers, since some compilers get confused by the fact that there's a local variable and static function by the same name. By using Fraction::gcd, we make it clear that the right hand side of the expression is a function call, not a reference to the variable we just defined.

  • SJ

    Hi Alex,

    Above in the lesson you have a line in both the operator<< and operator>> sections that say

    but they should be

  • imranIQ

    #include <iostream>
    using namespace std;
    class DatA{
          int day,month,year;
          public:
                 DatA(int x = 0,int y = 0, int z= 0): day(x),month(y),year(z)
                 {
                 }
          friend iostream& operator<<(iostream &out,const DatA &obj);
                 };
          iostream& operator<<(iostream &out,const DatA &obj)
          {
                    out<<"Date is "<<obj.day<<"-"<<obj.month<<"-"<<obj.year<<endl;
                    return out;
                    }
          int main(){
              DatA Pakistan(4,17,2016);
              cout<<Pakistan<<endl;
              system("pause");
              }
    error :no match for "operator<<" in "std::cout<<Pakistan"

    when i try to compile this code system gives me mentioned error i tried my best but no mistake found . plz any can highlight the problem

Leave a Comment

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