Если возможно, как функции, не являющиеся членами и не являющиеся друзьями.
Как описано Хербом Саттером и Скоттом Мейерсом, предпочитайте функции-члены, не являющиеся друзьями, функциям-членам, чтобы улучшить инкапсуляцию.
В некоторых случаях, например с потоками C ++, у вас не будет выбора, и вам придется использовать функции, не являющиеся членами.
Но все же это не означает, что вы должны сделать эти функции друзьями ваших классов: эти функции могут по-прежнему получать доступ к вашему классу через ваши методы доступа к классу. Если вам удастся написать эти функции таким образом, вы выиграете.
О прототипах оператора << и >>
Я считаю, что примеры, которые вы привели в своем вопросе, неверны. Например;
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Я даже не могу представить, как этот метод может работать в потоке.
Вот два способа реализации операторов << и >>.
Допустим, вы хотите использовать объект типа T, похожий на поток.
И что вы хотите извлечь / вставить из / в T соответствующие данные вашего объекта типа 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 ;
}
Универсальные прототипы методов оператора << и >>
Второй - как методы:
// 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 ;
}
Обратите внимание, что для использования этой записи вы должны расширить объявление класса T. Для объектов STL это невозможно (вы не должны изменять их ...).
А что, если T - это поток C ++?
Вот прототипы тех же операторов << и >> для потоков C ++.
Для общих basic_istream и basic_ostream
Обратите внимание, что это случай с потоками, поскольку вы не можете изменить поток C ++, вы должны реализовать функции. Что означает что-то вроде:
// OUTPUT << Paragraph
template <typename charT, typename traits>
std::basic_ostream<charT,traits> & operator << (std::basic_ostream<charT,traits> & p_oOutputStream, const Paragraph & p_oParagraph)
{
// do the insertion of p_oParagraph
return p_oOutputStream ;
}
// INPUT >> Paragraph
template <typename charT, typename traits>
std::basic_istream<charT,traits> & operator >> (std::basic_istream<charT,traits> & p_oInputStream, const CMyObject & p_oParagraph)
{
// do the extract of p_oParagraph
return p_oInputStream ;
}
Для char istream и ostream
Следующий код будет работать только для потоков на основе символов.
// 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 ;
}
Рис Улерих прокомментировал тот факт, что код на основе символов является всего лишь «специализацией» общего кода над ним. Конечно, Рис прав: я не рекомендую использовать пример на основе символов. Здесь он приводится только потому, что его проще читать. Поскольку это возможно только в том случае, если вы работаете только с потоками на основе символов, вам следует избегать его на платформах, где используется код wchar_t (например, в Windows).
Надеюсь, это поможет.