C++ FAQ Celebrating Twenty-One Years of the C++ FAQ!!!
(Click here for a personal note from Marshall Cline.)
Section 15:
[15.11] How can I provide printing for an entire hierarchy of classes?

Provide a friend operator<< that calls a protected virtual function:

class Base {
public:
  friend std::ostream& operator<< (std::ostream& o, Base const& b);
  ...
protected:
  virtual void printOn(std::ostream& o) const = 0;   or plain virtual; see below
};

inline std::ostream& operator<< (std::ostream& o, Base const& b)
{
  b.printOn(o);
  return o;
}

class Derived : public Base {
public:
  ...
protected:
  virtual void printOn(std::ostream& o) const;
};

void Derived::printOn(std::ostream& o) const
{
  ...
}
The end result is that operator<< acts as if it were dynamically bound, even though it's a friend function. This is called the Virtual Friend Function Idiom.

Note that derived classes override printOn(std::ostream&) const. In particular, they do not provide their own operator<<.

As to whether Base::printOn() is plain virtual or pure virtual, consider making it a plain virtual (without the "= 0") if you can implement that function with code that would otherwise be repeated in two or more derived classes. However if Base is a ABC with little or no member data, you might not be able to provide a meaningful definition for Base::printOn() and you should make it pure virtual. If you're not sure, make it pure virtual, at least until you get a better handle on the derived classes.