Подтвердить что ты не робот

Функции "друга" и "перегрузка оператора": каков надлежащий способ перегрузить оператор для класса?

В проекте, над которым я работаю, у меня есть класс Score, определенный ниже в score.h. Я пытаюсь перегрузить его так, когда на нем выполняется операция <<, печатается _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:: из объявления функции в score.cpp(фактически не объявляя его как член).

Почему это работает, но прежний фрагмент кода не работает?

Спасибо за ваше время!

ИЗМЕНИТЬ

Я удалил все упоминания о перегрузке в файле заголовка... но я получаю следующую (и единственную) ошибку. binary '<<' : no operator found which takes a right-hand operand of type 'Score' (or there is no acceptable conversion) Почему мой тест в main() не может найти соответствующую перегрузку? (это не включено, я проверил)

Ниже приведена полная оценка .h

#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
4b9b3361

Ответ 1

Примечание. Возможно, вы захотите просмотреть часто задаваемые вопросы о перегрузке оператора .


Двоичные операторы могут быть либо членами их левого класса аргументов, либо свободными функциями. (Некоторые операторы, такие как присвоение, должны быть членами.) Поскольку левый аргумент операторов потока является потоком, потоковые операторы либо должны быть членами класса потока, либо свободными функциями. Канонический способ реализации operator<< для любого типа таков:

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

Обратите внимание, что это не функция-член. Также обратите внимание, что объект передает поток в ссылку const. Это потому, что вы не хотите копировать объект для его потока, и вы также не хотите, чтобы потоковая передача тоже изменила его.


Иногда вы хотите передавать объекты, чьи внутренние объекты недоступны через открытый интерфейс их класса, поэтому оператор не может их получить. Тогда у вас есть два варианта: либо поместить открытый член в класс, который выполняет потоковое вещание

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;
}

или сделайте оператор a 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;
}

Ответ 2

Скажем, вы хотели написать перегрузку оператора для +, чтобы вы могли добавить два объекта Score друг к другу, а другой - добавить int в Score, а третий - так, чтобы вы может добавить a Score в int. Те, где a Score - первый параметр, могут быть функциями-членами Score. Но тот, где a int является первым параметром, не может стать функциями-членами int, правильно? Чтобы помочь вам в этом, вам разрешено писать их как бесплатные функции. Это то, что происходит с этим оператором <<, вы не можете добавить функцию-член в ostream, чтобы написать свободную функцию. Что это означает, когда вы убираете часть Score::.

Теперь почему это должно быть friend? Это не так. Вы вызываете только общедоступные методы (getPoints и scoreGetName). Вы видите множество операторов друзей, потому что им нравится говорить напрямую с частными переменными. У меня все в порядке, потому что они написаны и поддерживаются человеком, поддерживающим класс. Просто не получайте часть друга, запутанную с помощью функции-функции-vs-free-function.

Ответ 3

Вы получаете ошибки компиляции, когда operator<< является функцией-членом в примере, потому что вы создаете operator<<, который принимает Score как первый параметр (объект, вызывающий метод), и затем добавив дополнительный параметр в конец.

Когда вы вызываете двоичный оператор, объявленный как функция-член, левая часть выражения является объектом, вызывающим метод. например a + b может работать следующим образом:

A a;
B b

a.operator+(b)

Обычно предпочтительнее использовать не-членные двоичные операторы (а в некоторых случаях - например, operator<< для ostream - единственный способ сделать это. В этом случае a + b может работать следующим образом:

A a;
B b

operator+(a, b);

Вот полный пример, показывающий оба способа сделать это; main() будет выводить "55" три раза:

#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;
}