Domanda

Questa è fondamentalmente la domanda, c'è un "giusto"? modo di implementare operator < < ? Leggendo questo vedo che qualcosa del genere:

friend bool operator<<(obj const& lhs, obj const& rhs);

è preferito a qualcosa del genere

ostream& operator<<(obj const& rhs);

Ma non riesco proprio a capire perché dovrei usare l'uno o l'altro.

Il mio caso personale è:

friend ostream & operator<<(ostream &os, const Paragraph& p) {
    return os << p.to_str();
}

Ma probabilmente potrei fare:

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

Su quale logica devo basare questa decisione?

Nota :

 Paragraph::to_str = (return paragraph) 

dove il paragrafo è una stringa.

È stato utile?

Soluzione

Il problema qui è nella tua interpretazione dell'articolo link .

Questo articolo riguarda qualcuno che sta riscontrando problemi nel definire correttamente gli operatori delle relazioni bool.

L'operatore:

  • Uguaglianza == e! =
  • Relazione < & Gt; < = > =

Questi operatori dovrebbero restituire un bool in quanto stanno confrontando due oggetti dello stesso tipo. Di solito è più semplice definire questi operatori come parte della classe. Questo perché una classe è automaticamente amica di se stessa, quindi gli oggetti di tipo Paragrafo possono esaminarsi a vicenda (anche i membri privati ??degli altri).

C'è un argomento per rendere queste funzioni indipendenti in quanto ciò consente alla conversione automatica di convertire entrambi i lati se non sono dello stesso tipo, mentre le funzioni membro consentono solo la conversione automatica di rhs. Trovo che questo sia un argomento da uomo di carta poiché non vuoi davvero che la conversione automatica avvenga in primo luogo (di solito). Ma se questo è qualcosa che desideri (non lo consiglio), rendere i comparatori indipendenti può essere vantaggioso.

Gli operatori di stream:

  • operatore < < uscita
  • operatore > > ingresso

Quando li usi come operatori di flusso (anziché come spostamento binario) il primo parametro è un flusso. Dal momento che non si ha accesso all'oggetto stream (non è il tuo da modificare), questi non possono essere operatori membri, devono essere esterni alla classe. Pertanto devono essere amici della classe o avere accesso a un metodo pubblico che eseguirà lo streaming per te.

È inoltre tradizionale che questi oggetti restituiscano un riferimento a un oggetto stream in modo da poter collegare insieme le operazioni dello stream.

#include <iostream>

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;
}

Altri suggerimenti

Non puoi farlo come una funzione membro, perché il parametro this implicito è il lato sinistro dell'operatore < < . (Quindi, dovresti aggiungerlo come funzione membro alla classe ostream . Non va bene :)

Potresti farlo come una funzione gratuita senza amico ingerirlo? Questo è ciò che preferisco, perché chiarisce che si tratta di un'integrazione con ostream e non una funzionalità di base della tua classe.

Se possibile, come funzioni non membro e non amico.

Come descritto da Herb Sutter e Scott Meyers, preferiscono le funzioni non membro non amico alle funzioni membro, per aiutare ad aumentare l'incapsulamento.

In alcuni casi, come i flussi C ++, non avrai la scelta e dovrai usare funzioni non membro.

Tuttavia, ciò non significa che devi rendere queste funzioni amiche delle tue classi: queste funzioni possono ancora accedere alla tua classe attraverso i tuoi accessori di classe. Se riesci a scrivere queste funzioni in questo modo, allora hai vinto.

Informazioni sull'operatore < < e > > prototipi

Credo che gli esempi che hai fornito nella tua domanda siano sbagliati. Ad esempio;

ostream & operator<<(ostream &os) {
    return os << paragraph;
}

Non riesco nemmeno a pensare a come questo metodo potrebbe funzionare in un flusso.

Ecco i due modi per implementare < < e > > operatori.

Supponiamo che tu voglia usare un oggetto simile al flusso di tipo T.

E che si desidera estrarre / inserire da / in T i dati rilevanti dell'oggetto di tipo Paragrafo.

Operatore generico < < e > > prototipi di funzioni

Il primo essere 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 metodo

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 T. Per gli oggetti STL, ciò non è possibile (non è necessario modificarli ...).

E se T fosse un flusso C ++?

Ecco i prototipi dello stesso < < e > > operatori per flussi C ++.

Per basic_istream generico e basic_ostream

Nota che è il caso dei flussi, poiché non puoi modificare il flusso C ++, devi implementare le funzioni. Il che significa qualcosa del tipo:

// 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 charstream e ostream

Il seguente codice funzionerà solo per 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 il fatto che il codice basato sul carattere non è che una "specializzazione" del codice generico sopra di esso. Certo, Rhys ha ragione: non raccomando l'uso dell'esempio basato sui caratteri. Viene fornito qui solo perché è più semplice da leggere. Poiché è praticabile solo se lavori solo con flussi basati su char, dovresti evitarlo su piattaforme in cui il codice wchar_t è comune (cioè su Windows).

Spero che questo possa aiutare.

Dovrebbe essere implementato come funzioni gratuite e non amichevoli, specialmente se, come al giorno d'oggi, l'output viene utilizzato principalmente per la diagnostica e la registrazione. Aggiungi gli accessori const per tutte le cose che devono andare nell'output, quindi chiedi all'output di chiamarli ed eseguire la formattazione.

Ho effettivamente iniziato a raccogliere tutte queste funzioni libere di output di ostream in un "ostreamhelpers" intestazione e file di implementazione, mantiene quella funzionalità secondaria lontana dal vero scopo delle classi.

La firma:

bool operator<<(const obj&, const obj&);

Sembra piuttosto sospetto, questo non si adatta alla convenzione stream né alla convenzione bit a bit, quindi sembra un caso di abuso che sovraccarica l'operatore, operator < dovrebbe restituire bool ma l'operatore < < dovrebbe probabilmente restituire qualcos'altro.

Se intendevi dire così:

ostream& operator<<(ostream&, const obj&); 

Quindi, poiché non è possibile aggiungere funzioni a ostream per necessità, la funzione deve essere una funzione gratuita, che si tratti di un amico o meno, a seconda di cosa deve accedere (se non è necessario accedere a membri privati ??o protetti non è necessario renderlo amico).

Solo per completezza, vorrei aggiungere che davvero puoi creare un operatore ostream & amp; operatore < < (ostream & amp; os) all'interno di una classe e può funzionare. Da quello che so non è una buona idea usarlo, perché è molto contorto e non intuitivo.

Supponiamo di avere questo codice:

#include <iostream>
#include <string>

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;
}

Quindi, per riassumere, puoi farlo, ma molto probabilmente non dovresti :)

Operatore

< < implementato come funzione amico:

#include <iostream>
#include <string>
using namespace std;

class Samp
{
public:
    int ID;
    string strName; 
    friend std::ostream& operator<<(std::ostream &os, const Samp& obj);
};
 std::ostream& operator<<(std::ostream &os, const Samp& obj)
    {
        os << obj.ID<< “ ” << obj.strName;
        return os;
    }

int main()
{
   Samp obj, obj1;
    obj.ID = 100;
    obj.strName = "Hello";
    obj1=obj;
    cout << obj <<endl<< obj1;

} 
  

USCITA: 100 Ciao 100 Ciao Premere un tasto qualsiasi per continuare ...

Questa può essere una funzione amico solo perché l'oggetto si trova sul lato destro dell'operatore < < e l'argomento cout si trova sul lato sinistro. Quindi questa non può essere una funzione membro della classe, può essere solo una funzione amico.

amico operatore = pari diritti della classe

friend std::ostream& operator<<(std::ostream& os, const Object& object) {
    os << object._atribute1 << " " << object._atribute2 << " " << atribute._atribute3 << std::endl;
    return os;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top