Se possível, como funções de não membro e não amigo.
Conforme descrito por Herb Sutter e Scott Meyers, preferem funções não pertencentes a não amigas a funções membro, para ajudar a aumentar o encapsulamento.
Em alguns casos, como fluxos C ++, você não terá a opção e deve usar funções que não são membros.
Mas, ainda assim, isso não significa que você precise tornar essas funções amigas de suas classes: Essas funções ainda podem acessar sua classe por meio de seus acessadores de classe. Se você conseguir escrever essas funções dessa maneira, você venceu.
Sobre os protótipos << e >> do operador
Eu acredito que os exemplos que você deu na sua pergunta estão errados. Por exemplo;
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Não consigo nem pensar em como esse método poderia funcionar em um fluxo.
Aqui estão as duas maneiras de implementar os operadores << e >>.
Digamos que você queira usar um objeto semelhante a um fluxo do tipo T.
E que você deseja extrair / inserir de / em T os dados relevantes do seu objeto do tipo Parágrafo.
Operador genérico << e >> protótipos de função
O primeiro sendo como funções:
// 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 ;
}
Operador genérico << e >> protótipos de método
O segundo sendo como métodos:
// 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 ;
}
Observe que, para usar essa notação, você deve estender a declaração de classe de T. Para objetos STL, isso não é possível (você não deve modificá-los ...).
E se T for um fluxo C ++?
Aqui estão os protótipos dos mesmos operadores << e >> para fluxos C ++.
Para basic_istream e basic_ostream genéricos
Observe que é o caso de fluxos, como você não pode modificar o fluxo C ++, você deve implementar as funções. O que significa algo como:
// 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 ;
}
Para char istream e ostream
O código a seguir funcionará apenas para fluxos baseados em 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 ;
}
Rhys Ulerich comentou sobre o fato de o código baseado em char ser apenas uma "especialização" do código genérico acima dele. Obviamente, Rhys está certo: eu não recomendo o uso do exemplo baseado em char. É fornecido apenas aqui porque é mais simples de ler. Como só é viável se você trabalhar apenas com fluxos baseados em char, evite-o em plataformas onde o código wchar_t é comum (por exemplo, no Windows).
Espero que isso ajude.