From Schmid.wiki
Jump to: navigation, search

ostream Output

The goal is to make a class outputtable to an ostream, like this:

A a;
cout << "The value of A is: " << a << endl;

Overloading operator <<

This can be achieved by overloading operator << to handle the A class. The operator will probably need access to private members of the class, so we declare it a friend of the class:

class A {
    friend ostream & operator << (ostream & out, const A &);
    int value_;
};
ostream & operator << (ostream & out, const A & a) {
    return out << a.value_;
}

The operator and friend must be declared for each class, which ends up being pretty error-prone.

operator << Explained

The operator can be used like this:

out << a << " " << b;

which is equivalent to:

(((out << a) << " " ) << b);

To clarify, we can replace operator<< with a print-method with the same type:

ostream & print(ostream & out, const A & a);

Which could be used like this:

print( print( print(out, a), " "), b);

Polymorphic ostream Output

operator<< Is Not Polymorphic

Defining operator<< for each class has a problem:

operator<< is not a method.

Printing an instance of a class will always use the static type of the instance, not the dynamic type. Thus you cannot print objects polymorphically.

Take a look at the following example:

class A { };
ostream & operator <<(ostream & out, const A & a) {
    out << "I am an A"; return out;
}
class B : public A { };
ostream & operator <<(ostream & out, const B & b) {
    out << "I am a B"; return out;
}

B b;
A & a = b;
cout << a << endl;

The result is:

I am an A

even though a is an instance of class B.

Printing Using a Method

To use polymorphism, we have to make operator<< a method instead. Here is a naive example:

class A {
public:
    virtual ostream & print( ostream & out ) const;
};
A a, b;
b.print( a.print( out ));

Using this approach is unbearably ugly and doesn't work with built-in types.

Solution

The solution for this problem is a combination of a method and an operator<<.

class A {
public:
    virtual void print(ostream & out) const {
        out << "I am an A";
    }
};
inline ostream & operator << ( ostream & out, const A & a ) {
    a.print(out); return out;
}

All printable base classes should have an operator << that calls print on the second argument. Thus, we get the nice syntax, polymorphism, and as an added bonus, we can completely drop the friend declaration, because print is now a method.

Polymorphism works, as you can see from this example:

class A {
public:
    virtual void print(ostream & out) const {
        out << "I am an A";
    }
};
ostream & operator <<(ostream & out, const A & a) {
    a.print(out); return out;
}
class B : public A {
public:
    virtual void print(ostream & out) const {
        out << "I am a B";
    }
};

B b;
A & a = b;
cout << a << endl;

Which outputs:

I am a B

Printable Interface

The ultimate solution reduces all the operator<< declarations to a single declaration. This is simply accomplished by declaring a Printable interface. To make a new class ostream outputtable, you only need to implement a print method. Here's the interface:

namespace N {
    class Printable { // interface class
    public:
        virtual ~Printable() {}
        virtual void print(ostream & out) const = 0;
    };
    inline ostream & operator << ( ostream & out, const Printable & p ) {
        p.print(out); return out;
    }
}

Declaring operator<< inside a namespace is important if you use other namespaces. It allows us to easily access it inside another namespace by 'using namespace N'.

Here's an example implementation of the Printable interface:

class A : Printable {
public:
    virtual void print(ostream & out) const { out << "I am an A"; }
};

And an example of outputting A inside another namespace:

namespace M {
    using namespace N; // get the operator<<
    A a;
    cout << a << endl;
}