12.10 — Printing inherited classes using operator<<

Consider the following program that makes use of a virtual function:

By now, you should be comfortable with the fact that b.print() will call Derived::print() (because b is pointing to a Derived class object, Base::print() is a virtual function, and Derived::print() is an override).

While calling member functions like this to do output is okay, this style of function doesn’t mix well with std::cout:

In this lesson, we’ll look at how to override operator<< for classes using inheritance, so that we can use operator<< as expected, like this:

The challenges with operator<<

Let’s start by overloading operator<< in the typical way:

Because there is no need for virtual function resolution here, this program works as we’d expect, and prints:


Now, consider the following main() function instead:

This program prints:


That’s probably not what we were expecting. This happens because our version of operator<< that handles Base objects isn’t virtual, so std::cout << bref calls the version of operator<< that handles Base objects rather than Derived objects.

Therein lies the challenge.

Can we make Operator << virtual?

If this issue is that operator<< isn’t virtual, can’t we simply make it virtual?

The short answer is no. There are a number of reasons for this.

First, only member functions can be virtualized -- this makes sense, since only classes can inherit from other classes, and there’s no way to override a function that lives outside of a class (you can overload non-member functions, but not override them). Because we typically implement operator<< as a friend, and friends aren’t considered member functions, a friend version of operator<< is ineligible to be virtualized. (For a review of why we implement operator<< this way, please revisit lesson 9.4 -- Overloading operators using member functions).

Second, even if we could virtualize operator<< there’s the problem that the function parameters for Base::operator<< and Derived::operator<< differ (the Base version would take a Base parameter and the Derived version would take a Derived parameter). Consequently, the Derived version wouldn’t be considered an override of the Base version, and thus be ineligible for virtual function resolution.

So what’s a programmer to do?

The solution

The answer, as it turns out, is surprisingly simple.

First, we set up operator<< as a friend in our base class as usual. But instead of having operator<< do the printing itself, we delegate that responsibility to a normal member function that can be virtualized!

Here’s the full solution that works:

The above program works in all three cases:


Let’s examine how in more detail.

First, in the Base case, we call operator<<, which calls virtual function print(). Since our Base reference parameter points to a Base object, b.print() resolves to Base::print(), which does the printing. Nothing too special here.

In the Derived case, the compiler first looks to see if there’s an operator<< that takes a Derived object. There isn’t one, because we didn’t define one. Next the compiler looks to see if there’s an operator<< that takes a Base object. There is, so the compiler does an implicit upcast of our Derived object to a Base& and calls the function (we could have done this upcast ourselves, but the compiler is helpful in this regard). This function then calls virtual print(), which resolves to Derived::print().

Note that we don’t need to define an operator<< for each derived class! The version that handles Base objects works just fine for both Base objects and any class derived from Base!

The third case proceeds as a mix of the first two. First, the compiler matches variable bref with operator<< that takes a Base. That calls our virtual print() function. Since the Base reference is actually pointing to a Derived object, this resolves to Derived::print(), as we intended.

Problem solved.

12.x -- Chapter 12 comprehensive quiz
12.9 -- Dynamic casting

31 comments to 12.10 — Printing inherited classes using operator<<

  • koe

    From 12.6 on interface classes: "An interface class is a class that has no member variables, and where all of the functions are pure virtual! In other words, the class is purely a definition, and has no actual implementation."

    Does this mean any inheritance tree with overloaded operator<< can't responsibly derive from an 'interface class' root? Or is the language in 12.6 too strict, and interface classes still qualify as interface classes with techniques like the one from this section?

  • hellmet

    Doesn't the upcast and the downcast impact the performance? I don't see any other elegant way to accomplish this. Considering not just the << operator, but also +, += etc which are more frequently used...

    Also, do these features really impact the performance _that_ much? I tried looking around, but couldn't get any insight. I mean, C++ is known for it's blistering speed after C, so should I really be worried?

    • nascardriver

      We're not creating any copies when casting references, so those casts have no cost. If the parameters weren't references, then the code would hit heavy on performance, because it has to create a copy for every operator call.

      > I mean, C++ is known for it's blistering speed after C, so should I really be worried?
      There's no magic in C++ that makes it faster than other compiled languages. It can be fast, because the programmer has a lot of control over what happens. Bad code results in poor performance, but if you know how to use the language properly, you'll write fast programs.

      • hellmet

        > We're not creating any copies when casting references
        Ohh correct! It slipped my mind! *Facepalm*

        Could you perhaps give some insight on how much additional delay a virtual table introduces, if possible?

        > It can be fast, because the programmer has a lot of control
        Here's hoping I write good programs then!

        • nascardriver

          In addition to the cost of a regular call (with early binding (ie. we're calling a function by name, not through a pointer)), there is

          - 1 indirection (Reading the vtable pointer from the object)
          - 1 addition (Calculating the element in the vtable)
          - 1 indirection (Reading the function pointer from the vtable (This step is free if we're comparing a virtual call to a call with late binding)).

          In x86, a virtual call needs at least 4 instructions (pass this, indirection, addition and indirection, call), whereas a regular member function call needs 2 (pass this, call).
          The actual delay caused by this depends on your CPU. This should be one of the later things to consider during optimization. Non-virtual approaches (eg. via templates (covered later)) can quickly reduce the readability of your code.

  • Anthony

    Can't one just do this?

    • Alex

      Definitely. But that only works because getName() is the only thing that varies between classes. If the output from each class varies more significantly, then the method shown in the lesson will be more flexible.

  • lucieon

    "In the Derived case, the compiler first looks to see if there’s an operator<< that takes a Derived object. There isn’t one, because we didn’t define one. Next the compiler looks to see if there’s an operator<< that takes a Base object."
    Does the compiler check this for every function/operator or is operator<< a special case?
    For example;
    If you have a friend print() function in Base class and you call d.print() where d is a Derived class object, does the compiler check for print() in Base class if it doesn't find one in Derived class?

  • zakaria

    I have a similar but not exact problem in one TD, out TD "exercise" id given just after inheritance, so the virtual is not known yet, I can't use it.

    I have class Groupe which contains a vector of pointers towards users, I want to print every user in the list but some are premium users and some are regular both are child's from base user class.

    ostream & operator<<(ostream& os, const Groupe& groupe)
        os << "L'evenement " << groupe.nom_ << " a coute un total de " << groupe.getTotalDepenses() << " :  \n\n";
        for (unsigned i = 0; i < groupe.utilisateurs_.size(); i++) {
            os << *groupe.utilisateurs_[i];
        os << endl;

        if (groupe.transferts_.size() != 0) {
            os << "Les transferts suivants ont ete realiser pour equilibrer  : " << endl;
            for (unsigned i = 0; i < groupe.transferts_.size(); i++) {
                os << "\t";
                os << *groupe.transferts_[i];
            cout << endl;
        else {
            os << "Soit les comptes suivants :" << endl;
            for (unsigned i = 0; i < groupe.utilisateurs_.size(); i++) {
                os << groupe.utilisateurs_[i]->getNom() << " : " << groupe.comptes_[i] << endl;
        os << endl;

        return os;

    and the operator << for userPremium is :
    ostream & operator<<(ostream & os, const UtilisateurPremium & utilisateur)
        Utilisateur ut = static_cast<Utilisateur>(utilisateur);
        os << ut;
        os << endl << "Taux :" << utilisateur.taux_
           << "Nbr jours :" << utilisateur.joursRestants_;//????? need for virtual
        return os;

    and siilar one for regular ,

    the mother user printing is :
    ostream& operator<<(ostream& os, const Utilisateur& utilisateur)  
        os << "Utilisateur : " << utilisateur.nom_ << " (" << utilisateur.valeurEnum(utilisateur.type_) <<")" << " a depense pour un total de : "
        << utilisateur.getTotalDepenses() << endl;
        os << "\t Liste de depenses : " << endl;

        for (unsigned i = 0; i < utilisateur.depenses_.size(); i++) {
            os << "\t\t";
            os << *utilisateur.depenses_[i];
        os << "Interet :" << utilisateur.interet_<< endl;

        return os;

    the proplem is the printout is as follow :

    L'evenement Madrid a coute un total de 1480 :

    Utilisateur : alex (Premium) a depense pour un total de : 1050
             Liste de depenses :
                    Achat : d6 Prix : 1000 Type : groupe;
                    Achat : d8 Prix : 50 Type : groupe;
    Interet :8.66667
    Utilisateur : Max (Premium) a depense pour un total de : 1480
             Liste de depenses :
                    Achat : d6 Prix : 1000 Type : groupe;
                    Achat : d7 Prix : 430 Type : groupe;
                    Achat : d8 Prix : 50 Type : groupe; ........ect

    the program see my premium users as basic users,  so it neglects the particular additional printing for premium which is os <<"Taux:" << utilisateur.taux_<< "Nbr jours :" << utilisateur.joursRestants_;

    how can I fix this !! to make the program print the regular as regular and premium as premium without doing it manually in the Group print with if condition to ass the extra values for regular or premium.

    thanks for reading, and I appreciate your reply because its a very complicated TD contains 19 CPP and .h files

  • Ram

    Hi Alex,

    I have one question.

    You mentioned as below.

    In the Derived case, the compiler first looks to see if there’s an operator<< that takes a Derived object. There isn’t one, because we didn’t define one. Next the compiler looks to see if there’s an operator<< that takes a Base object. There is, so the compiler does an implicit upcast of our Derived object to a Base& and calls the function

    So my question :: does above thing just applicable to << operator, or does it applicable to all other operator as well ?

  • [code]
    friend std::ostream& operator<<(std::ostream &out, const Base &b)
            // Delegate printing responsibility for printing to member function print()
            return b.print(out);

    Why dont we need to have the this* pointer in the parameter in the print function

    • nascardriver

      Hi Aron!

      The this pointer is automatically passed behind the scenes, you don't need to do it manually. In your example a pointer to @b is passed as the 'this' parameter to @print.

      PS: Closing code tags use forward slashes

  • Sivasankar

    Hi Alex,

    Thanks for your awesome tutorials. In the 1st example, there is a code snippet as below

    There are no initializations required for Base class. Then is it mandatory to call Base() constructor in the initializer list of Derived class in this case?

  • lh

    I really like this method. I used it in my program and feel that it's more concise and beautiful now. Thanks very much!

  • Ninja

    Just to make sure, this is the template method design pattern right?

    • Alex

      I'm not sure this qualifies, since the template method design pattern defers some steps to the subclass. This essentially just routes the entire call to a virtualizable function.

  • Sandeep

    Hi Alex,

    I have same query as above if derived have not any function (operator<<) then how it will call from Derived ref.

    #include <iostream>
    using namespace std;
    class Base
        Base(){    }
        virtual void print() const
            cout<< "Base";
        friend ostream& operator<<(ostream &out, const Base &b)
            out<< "Base Overload  :: ";
            return out;
    class Derived : public Base
        Derived() : Base() {}
        virtual void print() const
            cout<< "Derived";
    /*    friend ostream& operator<<(ostream& out, const Derived &b)
            cout<< "Derived Overload" ;
            return out;
    int main()
        Derived d;
        Derived &b = d;
        cout<<"B is "<<b<<endl;
        return 0;

    • Alex

      This is easily answered by trying it.

      In this case, because there is no D::operator<<, it will call B:operator<<. B::operator<< will then call virtual function print(), which will resolve to D::print().

  • Surya

    Just out of curiosity. Can I do this?

    • Alex

      You can. But this is poor code for extensibility. Consider what would happen if you had a Derived2 that also derived from Base. If you passed in a Derived2, it will tell you it's a base when it's actually a Derived2. That's why if you need to do this kind of class identification, it's generally better to use a virtual function.

      • Fan

        I guess in the case of Derived2 it will print "derived" not "base" because a Base pointer to a Derived2 can be dynamically cast to a Derived pointer, right?

        • Alex

          What you get depends on whether Derived2 inherits from Base or Derived. In my previous response, I was assuming it derived from Base.

  • Sangita gupta

    Hi Alex,

    In your solution example, I have a doubt. I think friend function does not get inherited, then how does base class operator<<() function gets called?

    • Alex

      Subclasses don't inherit friend associations (although our operator<&lt is a friend of Base, that operator is not a friend of Derived just because Derived inherits Base).

      But that isn't relevant here. What is relevant here is that an object of type Derived can be trivially upcast to an object of type Base, so there is an version of operator<&lt that can matches our function call to std::cout << d.

  • Prado


    You wrote "operator<&lt" instead of "operator<<"

Leave a Comment

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