Функции «друга» и << перегрузка операторов: как правильно перегрузить оператор для класса? - PullRequest
20 голосов
/ 13 мая 2010

В проекте, над которым я работаю, у меня есть класс 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 (фактически не объявляя его членом).

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

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

EDIT

Я удалил все упоминания о перегрузке в заголовочном файле ... но я получаю следующую (и единственную) ошибку. 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 ]

57 голосов
/ 13 мая 2010

Примечание: Возможно, вы захотите взглянуть на 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;
}
9 голосов
/ 13 мая 2010

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

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

6 голосов
/ 13 мая 2010

Вы получаете ошибки компиляции, когда 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...