Se possibile, come funzioni non membro e non amico.
Come descritto da Herb Sutter e Scott Meyers, preferisci le funzioni non membro non amiche alle funzioni membro, per aumentare l'incapsulamento.
In alcuni casi, come gli stream C ++, non avrai scelta e dovrai usare funzioni non membri.
Tuttavia, ciò non significa che devi rendere queste funzioni amiche delle tue classi: queste funzioni possono ancora accedere alla tua classe tramite le funzioni di accesso alle classi. Se riesci a scrivere quelle funzioni in questo modo, hai vinto.
Informazioni sui prototipi << e >> dell'operatore
Credo che gli esempi che hai fornito nella tua domanda siano sbagliati. Per esempio;
ostream & operator<<(ostream &os) {
return os << paragraph;
}
Non riesco nemmeno a pensare a come potrebbe funzionare questo metodo in un flusso.
Ecco i due modi per implementare gli operatori << e >>.
Supponiamo che tu voglia utilizzare un oggetto simile a un flusso di tipo T.
E che vuoi estrarre / inserire da / in T i dati rilevanti del tuo oggetto di tipo Paragrafo.
Prototipi di funzione operatore generico << e >>
Il primo è come funzioni:
// 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 ;
}
Operatore generico << e >> prototipi di metodi
Il secondo è come metodi:
// 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 ;
}
Nota che per usare questa notazione, devi estendere la dichiarazione di classe di T. Per gli oggetti STL, questo non è possibile (non dovresti modificarli ...).
E se T fosse uno stream C ++?
Di seguito sono riportati i prototipi degli stessi operatori << e >> per i flussi C ++.
Per basic_istream e basic_ostream generici
Nota che è il caso dei flussi, poiché non puoi modificare il flusso C ++, devi implementare le funzioni. Che significa qualcosa come:
// 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 ;
}
Per char istream e ostream
Il codice seguente funzionerà solo per i flussi basati su caratteri.
// 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 ha commentato sul fatto che il codice basato sui caratteri non è che una "specializzazione" del codice generico sopra di esso. Ovviamente Rhys ha ragione: non consiglio l'uso dell'esempio basato sui caratteri. Viene fornito qui solo perché è più semplice da leggere. Poiché è fattibile solo se lavori solo con flussi basati su caratteri, dovresti evitarlo su piattaforme in cui il codice wchar_t è comune (ad esempio su Windows).
Spero che questo ti aiuti.