Должен ли оператор << быть реализован как друг или как функция-член?
Это в основном вопрос: есть ли «правильный» способ 9X_cxx реализовать operator<<
? Читая this, я вижу что-то вроде:
friend bool operator<<(obj const& lhs, obj const& rhs);
предпочтительнее 9X_cpp чем-то вроде
ostream& operator<<(obj const& rhs);
Но я не совсем понимаю, зачем 9X_c++ мне использовать тот или иной.
Мой личный 9X_cxx случай:
friend ostream & operator<<(ostream &os, const Paragraph& p) { return os << p.to_str(); }
Но, наверное, я мог бы:
ostream & operator<<(ostream &os) { return os << paragraph; }
На каком основании 9X_c++ я должен принять это решение?
Примечание:
Paragraph::to_str = (return paragraph)
где абзац 9X_c++ - это строка.
- Кстати, вам, вероятно, с ...
Ответ #1
Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?
Проблема здесь в вашей интерпретации статьи, которую 9X_cxx вы link.
Равенство
Эта статья посвящена человеку, у которого 9X_cpp возникли проблемы с правильным определением 9X_c++ операторов отношений типа bool.
Оператор:
- Равенство == и! =
- Отношения <> <=> =
Эти 9X_operator-overloading операторы должны возвращать логическое значение, поскольку 9X_cxx они сравнивают два объекта одного типа. Обычно 9X_operator-overloading эти операторы проще всего определить как 9X_operator-overloading часть класса. Это связано с тем, что класс 9X_cxx автоматически является другом самого себя, поэтому 9X_c++ объекты типа Paragraph могут проверять друг 9X_cxx друга (даже частные члены друг друга).
Существует 9X_operator-overloading аргумент в пользу создания этих автономных 9X_c++ функций, поскольку это позволяет автоматическому 9X_cpp преобразованию преобразовывать обе стороны, если 9X_cpp они не одного типа, в то время как функции-члены 9X_operator-overloading позволяют автоматически преобразовывать 9X_cpp только правая часть. Я считаю, что это аргумент 9X_c++ бумажного человека, поскольку вы действительно 9X_operator-overloading не хотите, чтобы автоматическое преобразование 9X_cxx происходило в первую очередь (обычно). Но 9X_cpp если это то, что вам нужно (я не рекомендую), то 9X_c++ сделать компараторы отдельно стоящими может 9X_cxx быть выгодно.
Потоковая передача
Операторы потока:
- оператор << вывод
- оператор >> ввод
Когда вы используете 9X_operator-overloading их в качестве операторов потока (а не двоичного 9X_c++ сдвига), первым параметром является поток. Поскольку 9X_c++ у вас нет доступа к объекту потока (его 9X_cxx нельзя изменять), они не могут быть операторами-членами, они 9X_c++ должны быть внешними по отношению к классу. Таким 9X_c++ образом, они должны либо быть друзьями класса, либо 9X_operator-overloading иметь доступ к общедоступному методу, который 9X_operator-overloading будет выполнять потоковую передачу за вас.
Эти 9X_c++ объекты также традиционно возвращают ссылку 9X_operator-overloading на объект потока, чтобы можно было связать 9X_c++ операции потока вместе.
#include class Paragraph { public: explicit Paragraph(std::string const& init) :m_para(init) {} std::string const& to_str() const { return m_para; } bool operator==(Paragraph const& rhs) const { return m_para == rhs.m_para; } bool operator!=(Paragraph const& rhs) const { // Define != operator in terms of the == operator return !(this->operator==(rhs)); } bool operator<(Paragraph const& rhs) const { return m_para < rhs.m_para; } private: friend std::ostream & operator<<(std::ostream &os, const Paragraph& p); std::string m_para; }; std::ostream & operator<<(std::ostream &os, const Paragraph& p) { return os << p.to_str(); } int main() { Paragraph p("Plop"); Paragraph q(p); std::cout << p << std::endl << (p == q) << std::endl; }
- @MattClarkson: Это не так. Его объявление функции друга, таким образом, не является частью класса и, следовательно, не зависит от спецификаторов доступа. Обычно ...
Ответ #2
Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?
Вы не можете сделать это как функцию-член, потому 9X_cpp что неявный параметр this
- это левая часть 9X_cxx оператора <<
. (Следовательно, вам нужно будет 9X_cxx добавить его как функцию-член к ostream
-классу. Не 9X_c++ очень хорошо :)
Могли бы вы сделать это как 9X_c++ бесплатную функцию без использования friend
? Это 9X_cpp то, что я предпочитаю, потому что это дает 9X_c++ понять, что это интеграция с ostream
, а не основная 9X_c++ функциональность вашего класса.
- @xaxxon Думаю, мое первое предложение объясняет, почему в этом случае невозможно добавить функцию как функцию-член. Функция «друг» имеет те же права, что и фун ...
Ответ #3
Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?
Если возможно, как функции, не являющиеся членами и не являющиеся друзьями.
Как описано Хербом Саттером и Скоттом Мейерсом, для 9X_cxx улучшения инкапсуляции предпочитайте функции, не 9X_cxx являющиеся членами друзей, функциям-членам, а 9X_cpp не функциям-членам.
В некоторых случаях, например 9X_c++ с потоками C++, у вас не будет выбора, и 9X_operator-overloading вам придется использовать функции, не являющиеся 9X_cxx членами.
Но все же это не означает, что вы 9X_cpp должны сделать эти функции друзьями ваших 9X_cpp классов: эти функции могут по-прежнему получать 9X_cpp доступ к вашему классу через ваши методы 9X_operator-overloading доступа к классу. Если вам удастся написать 9X_cpp эти функции таким образом, вы выиграете.
Об операторе << и >> прототипы
Я 9X_operator-overloading считаю, что приведенные вами в вопросе примеры 9X_cpp неверны. Например;
ostream & operator<<(ostream &os) { return os << paragraph; }
Я даже не могу представить, как 9X_cxx этот метод может работать в потоке.
Вот два 9X_cxx способа реализации операторов << и >>.
Допустим, вы 9X_c++ хотите использовать объект типа T, похожий 9X_cpp на поток.
И что вы хотите извлечь / вставить 9X_cpp из / в T соответствующие данные вашего объекта 9X_operator-overloading типа Paragraph.
Универсальный оператор << и >> прототипы функций
Первый - как функции:
// T << Paragraph T & operator << (T & p_oOutputStream, const Paragraph & p_oParagraph) { // do the insertion of p_oParagraph return p_oOutputStream ; } // T >> Paragraph T & operator >> (T & p_oInputStream, const Paragraph & p_oParagraph) { // do the extraction of p_oParagraph return p_oInputStream ; }
Универсальный оператор << и >> прототипы методов
Второй 9X_cpp - как методы:
// T << Paragraph T & T::operator << (const Paragraph & p_oParagraph) { // do the insertion of p_oParagraph return *this ; } // T >> Paragraph T & T::operator >> (const Paragraph & p_oParagraph) { // do the extraction of p_oParagraph return *this ; }
Обратите внимание, что для 9X_c++ использования этой записи вы должны расширить 9X_cpp объявление класса T. Для объектов STL это 9X_cpp невозможно (вы не должны изменять их ...).
А что, если T - это поток C++?
Вот 9X_cxx прототипы тех же операторов << и >> для 9X_cpp потоков C++.
Для общего потока basic_istream и basic_ostream
Обратите внимание, что в случае 9X_cxx с потоками, поскольку вы не можете изменить 9X_operator-overloading поток C++, вы должны реализовать функции. Что 9X_operator-overloading означает что-то вроде:
// OUTPUT << Paragraph template std::basic_ostream & operator << (std::basic_ostream & p_oOutputStream, const Paragraph & p_oParagraph) { // do the insertion of p_oParagraph return p_oOutputStream ; } // INPUT >> Paragraph template std::basic_istream & operator >> (std::basic_istream & p_oInputStream, const CMyObject & p_oParagraph) { // do the extract of p_oParagraph return p_oInputStream ; }
Для char istream и ostream
Следующий код будет 9X_cxx работать только для потоков на основе символов.
// OUTPUT << A std::ostream & operator << (std::ostream & p_oOutputStream, const Paragraph & p_oParagraph) { // do the insertion of p_oParagraph return p_oOutputStream ; } // INPUT >> A std::istream & operator >> (std::istream & p_oInputStream, const Paragraph & p_oParagraph) { // do the extract of p_oParagraph return p_oInputStream ; }
Рис 9X_cpp Улерих прокомментировал тот факт, что код 9X_cxx на основе символов является всего лишь «специализацией» общего 9X_c++ кода над ним. Конечно, Рис прав: я не рекомендую 9X_cxx использовать пример на основе символов. Он 9X_c++ приводится здесь только потому, что его 9X_cpp легче читать. Поскольку это возможно только 9X_cpp в том случае, если вы работаете только с 9X_cxx потоками на основе символов, вам следует 9X_operator-overloading избегать его на платформах, где используется 9X_cpp код wchar_t (например, в Windows).
Надеюсь, это 9X_cxx поможет.
Ответ #4
Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?
Его следует реализовать как бесплатную функцию, не 9X_cpp являющуюся дружественной, особенно если, как 9X_operator-overloading и в большинстве случаев в наши дни, вывод 9X_c++ в основном используется для диагностики 9X_cpp и ведения журнала. Добавьте константные 9X_cpp аксессоры для всех вещей, которые должны 9X_cpp поступать в вывод, а затем пусть средство 9X_c++ вывода просто вызовет их и выполнит форматирование.
На 9X_cxx самом деле я собрал все эти бесплатные функции 9X_operator-overloading вывода ostream в заголовке "ostreamhelpers" и 9X_c++ файле реализации, это удерживает эту вторичную 9X_cpp функциональность далеко от реальной цели 9X_cpp классов.
Ответ #5
Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?
Подпись:
bool operator<<(const obj&, const obj&);
Кажется довольно подозрительным, это 9X_cpp не соответствует соглашению stream
и побитовым 9X_cxx соглашениям, поэтому похоже на случай злоупотребления 9X_c++ перегрузкой оператора, operator <
должен возвращать 9X_c++ bool
, но operator <<
, вероятно, должен возвращать что-то 9X_operator-overloading еще.
Если вы имели в виду, скажите:
ostream& operator<<(ostream&, const obj&);
Затем, поскольку 9X_cpp вы не можете добавлять функции в ostream
по необходимости, функция 9X_cpp должна быть бесплатной функцией, независимо 9X_operator-overloading от того, является ли она friend
или нет, зависит 9X_cpp от того, к чему у нее есть доступ (если 9X_c++ ей не нужен доступ к частным или защищенных 9X_c++ участников, нет необходимости дружить).
Ответ #6
Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?
В завершение я хотел бы добавить, что вы 9X_cxx действительно можете создать оператор ostream& operator << (ostream& os)
внутри 9X_c++ класса, и он может работать. Насколько я 9X_cxx знаю, использовать его - не лучшая идея, потому 9X_operator-overloading что он очень запутанный и не интуитивно 9X_c++ понятный.
Предположим, у нас есть этот код:
#include #include using namespace std; struct Widget { string name; Widget(string _name) : name(_name) {} ostream& operator << (ostream& os) { return os << name; } }; int main() { Widget w1("w1"); Widget w2("w2"); // These two won't work { // Error: operand types are std::ostream << std::ostream // cout << w1.operator<<(cout) << '\n'; // Error: operand types are std::ostream << Widget // cout << w1 << '\n'; } // However these two work { w1 << cout << '\n'; // Call to w1.operator<<(cout) returns a reference to ostream& w2 << w1.operator<<(cout) << '\n'; } return 0; }
Подводя 9X_cpp итог - вы можете это сделать, но, скорее 9X_cpp всего, не должны :)
-
3
-
11
-
6
-
5
-
6
-
5
-
4
-
3
-
5
-
4
-
4
-
4
-
2
-
2
-
5
-
6
-
3
-
2
-
5
-
2
-
3
-
3
-
6
-
3
-
3
-
5
-
5
-
4
-
4
-
10
-
3
-
11
-
3
-
4
-
4
-
3
-
7
-
3
-
2
-
2
-
4
-
4
-
4
-
3
-
3
-
11
-
6
-
3
-
4
-
4