По можливості, як функції, які не є членами та не є друзями.
Як описали Герб Саттер та Скотт Мейєрс, віддайте перевагу функціям, що не належать до друзів, ніж функціям членів, щоб сприяти збільшенню інкапсуляції.
У деяких випадках, як-от потоки C ++, у вас не буде вибору, і ви повинні використовувати функції, які не є членами.
Але все-таки це не означає, що ви повинні робити ці функції друзями своїх класів: Ці функції все ще можуть отримати доступ до вашого класу через ваші класові аксесуари. Якщо вам вдасться записати ці функції таким чином, ви виграли.
Про прототипи операторів << і >>
Я вважаю, що приклади, які ви навели у своєму запитанні, помиляються. Наприклад;
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Я навіть не можу почати думати, як цей метод може працювати в потоці.
Ось два способи реалізації операторів << і >>.
Скажімо, ви хочете використовувати потоковий об’єкт типу Т.
І що ви хочете витягнути / вставити з / в T відповідні дані вашого об'єкта типу абзацу.
Прототипи функціональних операторів << і >>
Перша істота як функції:
// 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
Наступний код буде працювати лише для потоків на основі char.
// 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 ;
}
Ріс Улеріч прокоментував той факт, що кодовий код є лише "спеціалізацією" загального коду над ним. Звичайно, Ріс має рацію: я не рекомендую використовувати приклад на основі char. Тут подано лише тому, що читати простіше. Оскільки він життєздатний лише в тому випадку, якщо ви працюєте лише з потоками на основі char, вам слід уникати цього на платформах, де звичайний код wchar_t (тобто в Windows).
Сподіваюсь, це допоможе.