継承クラスを出力演算子のオーバーロードで出力したい場合、operator<<自体を仮想関数にはできないため、仮想メンバ関数を中継に使う方法が一般的である。

operator<<は通常、非メンバ関数として定義する。
しかし、非メンバ関数は仮想関数にできない。

そのため、基底クラスに仮想のprint()のようなメンバ関数を用意し、operator<<の中からその関数を呼ぶ。

class Base
{
public:
    virtual void print(std::ostream& out) const
    {
        out << "Base";
    }
 
    friend std::ostream& operator<<(std::ostream& out, const Base& b)
    {
        b.print(out);
        return out;
    }
};
 
class Derived : public Base
{
public:
    void print(std::ostream& out) const override
    {
        out << "Derived";
    }
};

この場合、std::cout << derived;だけでなく、Base&Base*を通して扱っている場合でも、print()が仮想呼び出しされることで、実際の型に応じた出力が行われる。

基底クラスの出力を使い回す

派生クラスで基底クラス部分の出力も使いたい場合は、基底クラスのprint()を呼んでから派生クラス固有の情報を追加できる。

void print(std::ostream& out) const override
{
    Base::print(out);
    out << " Derived";
}

注意

  • operator<<自体は仮想関数にできない
  • 多態的な出力をしたい場合は、仮想メンバ関数を1つ中継に使う
  • 継承階層で出力を拡張しやすい

関連

参考