2013-04-03 23 views
6

2つのクラスがあり、2つのグローバルfriend oparator<<があります。グローバル演算子と多相

class A { 
    friend std::ostream& operator<<(std::ostream& o, const A &a); 
}; 

class B: public A { 
    friend std::ostream& operator<<(std::ostream& o, const B &b); 
}; 

私はこのようにそれを使用する場合は、すべてが正常に動作している、オペレータのBバージョンが呼び出されます:

B b; 
std::cout << b; 

しかし、私はポリモーフィズムを使用している場合、Aバージョンはダイナミックものの、呼び出されますタイプはBです:

A* b = new B(); 
std::cout << *b; 

一つの解決策は、鋳造されています

std::cout << static_cast<B&>(*b); 

しかし、これにはさらに簡単で洗練されたソリューションがありますか?

+2

'operator <<(std :: ostream&、A const&)'で仮想関数を呼び出すのはどうですか? – dyp

答えて

10

はい。 1つの出力演算子とvirtual printはクラスで機能します。あなたがvirtualとしてそれらを定義する場合は、基本クラスへのポインタを経由してアクセスする場合、コンパイラは手掛かりをしていないので、派生クラスの関数の

class A 
{ 
public: 
    virtual ~A() {} 
private: 
    virtual void print(std::ostream&) {} 
    friend std::ostream& operator << (std::ostream& os, const A& obj) 
    { 
     obj.print(os); 
     return os; 
    } 
}; 

class B 
{ 
private: 
    virtual void print(std::ostream&) {} 
}; 

Live example

+0

friend関数に 'return os;'を追加してください。 – scones

+1

その機能から 'friend'を削除してください - それは友人でなくても無料の機能です;) –

+0

@ArneMertz printはプライベートです。この場合、operator <<はどのようにフリー関数になりますか?そしてなぜ私たちは一般公開されている必要がありますか? – ForEveR

3

バージョンでのみと呼ばれるもののクラスポインタが指すオブジェクトは実際にはあります。

ここでトラブルが彼ら自身が仮想することはできませんので、あなたが友人の関数を定義していることで、解決策は単純です:Aのoperator<<コールあなたがBにオーバーロードすることができAにおける仮想関数の実装を持っています。

2

単に友達を使用しないでください。彼らは継承よりも緊密に結合しています。特に、クラステンプレートのフレンドオペレータを作成してそれを専門にし、クラスの内部へのアクセスを合法的に得ることができます。これらの理由から、私はこれらの演算子を構文的な砂糖としてのみ使用し、実際の作業を行うメンバ関数に委譲させます。その方法は、あなたの問題を解決するには非常に簡単ではありません:

class A { 
public: 
    virtual std::ostream& printToStream(std::ostream& os) const; 
}; 

std::ostream& operator<<(std::ostream& os, A const& a) 
{ return a.printToStream(os); } 

class B: public A { 
    virtual std::ostream& printToStream(std::ostream& os) const; 
}; 

は、CがAから派生別のクラスをお持ちですか?問題はなく、構文的な砂糖(つまりoperator<<)をもう一度定義する必要はなく、実際の作業の仕方、つまりオーバーライドprintToStreamを定義するだけです。これが仮想化された理由です。