web-dev-qa-db-ja.com

'friend'関数と<<演算子のオーバーロード:クラスの演算子をオーバーロードする適切な方法は何ですか?

私が取り組んでいるプロジェクトには、以下の_score.h_で定義されているScoreクラスがあります。私はそれをオーバーロードしようとしているので、_<<_操作がそれに実行されると、__points + " " + _name_が出力されます。

これが私がやろうとしたことです:

_ostream & Score::operator<< (ostream & os, Score right)
{
    os << right.getPoints() << " " << right.scoreGetName();
    return os;
}
_

返されるエラーは次のとおりです。

_score.h(30) : error C2804: binary 'operator <<' has too many parameters
_

(このエラーは実際には4回発生します)

オーバーロードをフレンド関数として宣言することで、なんとか機能させることができました。

_friend ostream & operator<< (ostream & os, Score right);
_

そして、score.cppの関数宣言から_Score::_を削除します(メンバーとして宣言しないこと)。

なぜこれが機能するのに、前のコードは機能しないのですか?

御時間ありがとうございます!

[〜#〜]編集[〜#〜]

ヘッダーファイルのオーバーロードへの言及をすべて削除しましたが、次の(そして唯一の)エラーが発生します。 binary '<<' : no operator found which takes a right-hand operand of type 'Score' (or there is no acceptable conversion) main()のテストで適切なオーバーロードが見つからないのはなぜですか? (インクルードではありません、チェックしました)

以下は完全なスコアです。

_#ifndef SCORE_H_
#define SCORE_H_

#include <string>
#include <iostream>
#include <iostream>

using std::string;
using std::ostream;

class Score
{

public:
    Score(string name);
    Score();
    virtual ~Score();
    void addPoints(int n);
    string scoreGetName() const;
    int getPoints() const;
    void scoreSetName(string name);
    bool operator>(const Score right) const;

private:
    string _name;
    int _points;

};
#endif
_
25
F. P.

注:演算子のオーバーロードに関するよくある質問 をご覧ください。


二項演算子は、左側の引数のクラスまたはフリー関数のメンバーにすることができます。 (割り当てなど、一部の演算子はメンバーでなければなりません。)ストリーム演算子の左側の引数はストリームであるため、ストリーム演算子は、ストリームクラスまたはフリー関数のメンバーでなければなりません。 operator<<を任意の型に実装する標準的な方法は次のとおりです。

std::ostream& operator<<(std::ostream& os, const T& obj)
{
   // stream obj's data into os
   return os;
}

メンバー関数ではないことに注意してください。また、const参照ごとにオブジェクトがストリーミングされることにも注意してください。これは、オブジェクトをストリーミングするためにオブジェクトをコピーする必要がなく、ストリーミングによってオブジェクトを変更する必要もないためです。


場合によっては、クラスのパブリックインターフェイスを介して内部にアクセスできないオブジェクトをストリーミングして、オペレーターがオブジェクトにアクセスできないようにする必要があります。次に、2つの選択肢があります:ストリーミングを行うクラスにパブリックメンバーを入れる

class T {
  public:
    void stream_to(std::ostream&) const {os << obj.data_;}
  private:
    int data_;
};

そしてそれをオペレーターから呼び出します:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
   obj.stream_to(os);
   return os;
}

または、演算子をfriendにします

class T {
  public:
    friend std::ostream& operator<<(std::ostream&, const T&);
  private:
    int data_;
};

クラスのプライベート部分にアクセスできるように:

inline std::ostream& operator<<(std::ostream& os, const T& obj)
{
   os << obj.data_;
   return os;
}
65
sbi

+の演算子オーバーロードを作成して、2つのScoreオブジェクトを互いに追加したり、intScoreに追加したりするとします。もう1つは、Scoreintに追加できるようにするためです。 Scoreが最初のパラメーターであるものは、スコアのメンバー関数にすることができます。しかし、intが最初のパラメーターであるものは、intのメンバー関数になれませんよね?それを助けるために、あなたはそれらを無料の関数として書くことが許されています。これが、この<<演算子で起こっていることです。メンバー関数をostreamに追加できないため、無料の関数を作成できます。 Score::部分を削除すると、それが意味されます。

なぜfriendにする必要があるのですか?そうではありません。パブリックメソッド(getPointsおよびscoreGetName)のみを呼び出しています。フレンドオペレーターは、プライベート変数と直接やり取りするのが好きなので、たくさんの友人オペレーターがいます。クラスを保守している人が書いて保守しているので、私がそれをしても大丈夫です。 member-function-vs-free-functionの部分と友達の部分を混同しないでください。

9
Kate Gregory

最初のパラメーターとしてScoreを受け取る_operator<<_を作成しているため、_operator<<_がメンバー関数である場合にコンパイルエラーが発生します(メソッドが呼び出されるオブジェクト)オン)、最後に追加のパラメーターを指定します。

メンバー関数として宣言されている2項演算子を呼び出す場合、式の左側はメソッドが呼び出されるオブジェクトです。例えば_a + b_は次のように機能します:

_A a;
B b

a.operator+(b)
_

通常、非メンバーの2項演算子を使用することをお勧めします(場合によっては、たとえば_operator<<_ for ostreamが唯一の方法です。その場合、_a + b_は次のように機能します:

_A a;
B b

operator+(a, b);
_

以下に、両方の方法を示す完全な例を示します。 main()は「55」を3回出力します。

_#include <iostream>

struct B
{
    B(int b) : value(b) {}
    int value;
};


struct A
{
    A(int a) : value(a) {}
    int value;

    int operator+(const B& b) 
    {
        return this->value + b.value;
    }
};

int operator+(const A& a, const B& b)
{
    return a.value + b.value;
}

int main(int argc, char** argv)
{
    A a(22);
    B b(33);

    std::cout << a + b << std::endl;
    std::cout << operator+(a, b) << std::endl;
    std::cout << a.operator+(b) << std::endl;

    return 0;
}
_
6
pkh