Функции друга и перегрузка оператора <<: Как правильно перегрузить оператор для класса?

В проекте, над которым я работаю, у меня есть 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() не может найти соответствующую перегрузку? (это не включает, я проверил)

Ниже приведен полный счет.

#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

3 ответа

Решение

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


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

std::ostream& operator<<(std::ostream& os, const T& obj)
{
   // stream obj's 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;
}

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

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

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

Вы получаете ошибки компиляции, когда 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;
}
Другие вопросы по тегам