Функции друга и перегрузка оператора <<: Как правильно перегрузить оператор для класса?
В проекте, над которым я работаю, у меня есть 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;
}