150

それが基本的な質問です。実装する「正しい」方法はありoperator<<ますか? これを読むと、次のようなことがわかります。

friend bool operator<<(obj const& lhs, obj const& rhs);

のようなものよりも優先されます

ostream& operator<<(obj const& rhs);

しかし、なぜどちらかを使用する必要があるのか​​ よくわかりません。

私の個人的なケースは次のとおりです。

friend ostream & operator<<(ostream &os, const Paragraph& p) {
    return os << p.to_str();
}

しかし、私はおそらくできるでしょう:

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

この決定はどのような根拠に基づいて行う必要がありますか?

:

 Paragraph::to_str = (return paragraph) 

段落は文字列です。

4

8 に答える 8

140

ここでの問題は、リンクする記事の解釈にあります。

平等

この記事は、bool 関係演算子を正しく定義する際に問題を抱えている人に関するものです。

オペレーター:

  • 等しい == と !=
  • 関係 < > <= >=

これらの演算子は、同じ型の 2 つのオブジェクトを比較しているため、bool を返す必要があります。通常、これらの演算子をクラスの一部として定義するのが最も簡単です。これは、クラスが自動的にそれ自体のフレンドであるため、Paragraph 型のオブジェクトが互いに (プライベート メンバー同士であっても) 調べることができるためです。

これらの独立した関数を作成することには議論があります。これにより、同じ型でない場合に両側が自動変換され、メンバー関数は rhs のみが自動変換されるようになります。そもそも(通常)自動変換が発生することを本当に望んでいないので、これはペーパーマンの議論だと思います。しかし、これが必要な場合 (私はお勧めしません)、コンパレーターを自立させると有利な場合があります。

ストリーミング

ストリーム演算子:

  • 演算子 << 出力
  • 演算子 >> 入力

これらを (バイナリ シフトではなく) ストリーム演算子として使用する場合、最初のパラメーターはストリームです。ストリーム オブジェクトへのアクセス権がない (変更するのはあなたのものではない) ため、これらはメンバ オペレータになることはできず、クラスの外部にある必要があります。したがって、それらはクラスのフレンドであるか、ストリーミングを行うパブリック メソッドにアクセスできる必要があります。

また、これらのオブジェクトがストリーム オブジェクトへの参照を返すことも従来から行われているため、ストリーム操作を連鎖させることができます。

#include <iostream>

class Paragraph
{
    public:
        explicit Paragraph(std::string const& init)
            :m_para(init)
        {}

        std::string const&  to_str() const
        {
            return m_para;
        }

        bool operator==(Paragraph const& rhs) const
        {
            return m_para == rhs.m_para;
        }
        bool operator!=(Paragraph const& rhs) const
        {
            // Define != operator in terms of the == operator
            return !(this->operator==(rhs));
        }
        bool operator<(Paragraph const& rhs) const
        {
            return  m_para < rhs.m_para;
        }
    private:
        friend std::ostream & operator<<(std::ostream &os, const Paragraph& p);
        std::string     m_para;
};

std::ostream & operator<<(std::ostream &os, const Paragraph& p)
{
    return os << p.to_str();
}


int main()
{
    Paragraph   p("Plop");
    Paragraph   q(p);

    std::cout << p << std::endl << (p == q) << std::endl;
}
于 2008-10-25T21:53:00.707 に答える
55

this暗黙のパラメーターは演算子の左側にあるため、メンバー関数として実行することはできません<<。(したがって、メンバー関数としてクラスに追加する必要がありますostream。良くありません:)

それをせずに無料の機能としてそれを行うことができますfriendか?これは との統合でostreamあり、クラスのコア機能ではないことが明確になるため、私はそれを好みます。

于 2008-10-25T18:24:38.317 に答える
31

できれば非会員・非フレンド機能として。

Herb Sutter と Scott Meyers によって説明されているように、カプセル化を促進するために、メンバー関数よりも非フレンド非メンバー関数を優先します。

C++ ストリームのように、選択の余地がなく、非メンバー関数を使用する必要がある場合もあります。

ただし、これらの関数をクラスのフレンドにする必要があるという意味ではありません。これらの関数は、クラス アクセサーを介してクラスにアクセスできます。これらの関数をこのように書くことに成功した場合、あなたは勝ちました。

演算子 << および >> プロトタイプについて

あなたの質問で挙げた例は間違っていると思います。例えば;

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

このメソッドがストリームでどのように機能するかを考え始めることさえできません。

<< および >> 演算子を実装する 2 つの方法を次に示します。

タイプ T のストリームのようなオブジェクトを使いたいとしましょう。

そして、タイプ Paragraph のオブジェクトの関連データを T から抽出/T に挿入したいとします。

ジェネリック演算子 << および >> 関数プロトタイプ

最初は関数としてです:

// T << Paragraph
T & operator << (T & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// T >> Paragraph
T & operator >> (T & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return p_oInputStream ;
}

汎用演算子 << および >> メソッドのプロトタイプ

2番目はメソッドとしてです:

// T << Paragraph
T & T::operator << (const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return *this ;
}

// T >> Paragraph
T & T::operator >> (const Paragraph & p_oParagraph)
{
   // do the extraction of p_oParagraph
   return *this ;
}

この表記法を使用するには、T のクラス宣言を拡張する必要があることに注意してください。STL オブジェクトの場合、これは不可能です (それらを変更することは想定されていません...)。

T が C++ ストリームの場合はどうなるでしょうか。

以下は、C++ ストリームの同じ << および >> 演算子のプロトタイプです。

汎用の basic_istream および basic_ostream の場合

ストリームの場合は C++ ストリームを変更できないため、関数を実装する必要があることに注意してください。つまり、次のような意味です。

// OUTPUT << Paragraph
template <typename charT, typename traits>
std::basic_ostream<charT,traits> & operator << (std::basic_ostream<charT,traits> & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> Paragraph
template <typename charT, typename traits>
std::basic_istream<charT,traits> & operator >> (std::basic_istream<charT,traits> & p_oInputStream, const CMyObject & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

char istream および ostream の場合

次のコードは、文字ベースのストリームに対してのみ機能します。

// OUTPUT << A
std::ostream & operator << (std::ostream & p_oOutputStream, const Paragraph & p_oParagraph)
{
   // do the insertion of p_oParagraph
   return p_oOutputStream ;
}

// INPUT >> A
std::istream & operator >> (std::istream & p_oInputStream, const Paragraph & p_oParagraph)
{
   // do the extract of p_oParagraph
   return p_oInputStream ;
}

Rhys Ulerich は、char ベースのコードはその上の汎用コードの「特殊化」にすぎないという事実についてコメントしました。もちろん、Rhys の言うとおりです。文字ベースの例の使用はお勧めしません。読みやすいので、ここでのみ示します。char ベースのストリームでのみ作業する場合にのみ実行可能であるため、wchar_t コードが一般的なプラットフォーム (つまり、Windows) では避ける必要があります。

これが役立つことを願っています。

于 2008-10-25T22:22:15.123 に答える
11

特に、最近のほとんどのものと同様に、出力が主に診断とログに使用される場合は、無料の非フレンド機能として実装する必要があります。出力に必要なすべてのものに const アクセサーを追加し、アウトプッターにそれらを呼び出してフォーマットを実行させます。

私は実際に、これらのすべての ostream 出力フリー関数を「ostreamhelpers」ヘッダーと実装ファイルに集めました。これにより、その二次機能がクラスの本来の目的から遠ざけられます。

于 2008-10-25T18:59:38.573 に答える
8

署名:

bool operator<<(const obj&, const obj&);

かなり疑わしいと思われますが、これは規則にもビットごとの規則にも適合しないため、stream演算子のオーバーロードの乱用のケースのように見えますが、おそらく何か他のものを返す必要operator <boolあります。operator <<

そのつもりなら、次のように言います。

ostream& operator<<(ostream&, const obj&); 

次に、必要に応じて関数を追加できないためostream、関数はフリー関数でなければなりfriendません。それがアクセスする必要があるかどうかによって異なります (プライベートまたは保護されたメンバーにアクセスする必要がない場合は、作成する必要はありません)。友人)。

于 2008-10-25T18:29:00.447 に答える
0

operator<<フレンド関数として実装:

#include <iostream>
#include <string>
using namespace std;

class Samp
{
public:
    int ID;
    string strName; 
    friend std::ostream& operator<<(std::ostream &os, const Samp& obj);
};
 std::ostream& operator<<(std::ostream &os, const Samp& obj)
    {
        os << obj.ID<< “ ” << obj.strName;
        return os;
    }

int main()
{
   Samp obj, obj1;
    obj.ID = 100;
    obj.strName = "Hello";
    obj1=obj;
    cout << obj <<endl<< obj1;

} 

出力:
100 こんにちは
100 こんにちは

オブジェクトが右側にありoperator<<、引数coutが左側にあるという理由だけで、これはフレンド関数になることができます。したがって、これはクラスのメンバー関数にすることはできません。フレンド関数にすることしかできません。

于 2012-01-20T11:15:09.857 に答える
0

友人演算子 = クラスと同等の権利

friend std::ostream& operator<<(std::ostream& os, const Object& object) {
    os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
    return os;
}
于 2016-02-01T12:02:36.600 に答える