Должен ли оператор << быть реализован как друг или как функция-член?

Это в основном вопрос: есть ли «правильный» способ 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++ - это строка.

159
2

  • Кстати, вам, вероятно, с ...
6
Общее количество ответов: 6

Ответ #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;
}

142
8

  • @MattClarkson: Это не так. Его объявление функции друга, таким образом, не является частью класса и, следовательно, не зависит от спецификаторов доступа. Обычно ...

Ответ #2

Ответ на вопрос: Должен ли оператор << быть реализован как друг или как функция-член?

Вы не можете сделать это как функцию-член, потому 9X_cpp что неявный параметр this - это левая часть 9X_cxx оператора <<. (Следовательно, вам нужно будет 9X_cxx добавить его как функцию-член к ostream -классу. Не 9X_c++ очень хорошо :)

Могли бы вы сделать это как 9X_c++ бесплатную функцию без использования friend? Это 9X_cpp то, что я предпочитаю, потому что это дает 9X_c++ понять, что это интеграция с ostream, а не основная 9X_c++ функциональность вашего класса.

58
2

  • @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 поможет.

32
0

Ответ #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 классов.

11
0

Ответ #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++ участников, нет необходимости дружить).

9
0

Ответ #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 всего, не должны :)

4
0