2010-05-13 2 views
18

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

Ниже приводится полный score.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 

ответ

54

Примечание: Вы можете посмотреть на operator overloading 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; 
} 
+0

Хороший, полный ответ. –

+0

Доступ к частным частям класса - Rawr. J/K, большое спасибо, но остается один вопрос: Я удалил все упоминания о перегрузке в файле заголовка ... но я получаю следующую (и только) ошибку. 'binary '<<': оператор не найден, который принимает правый операнд типа« Оценка »(или нет приемлемого преобразования)' Как мой тест в main() не может найти подходящий перегрузка? (это не включает). –

+1

Вы должны поместить объявление 'operator <<' right с определением вашего класса: то есть в том же заголовке и в том же пространстве имен. Таким образом, вы не забудете включить его, и вы, компилятор, сможете забрать его при поиске приемлемых перегрузок. –

9

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

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

+0

Спасибо за дополнительный ввод! –

6

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

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

A a; 
B b 

a.operator+(b) 

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

A a; 
B b 

operator+(a, b); 

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