Classi e operatori

di il
12 risposte

Classi e operatori

Dato il seguente codice:
//compl.h
class complesso
{
   double re, im;
public:
   complesso(double r=0, double i=0);
   double reale();
   double image();
   complesso operator+(complesso x);
   complesso operator+(double d);
}

//compl.cpp
#include...
...
   complesso::complesso(double r, double i) { re=r; im=i; }
   double complesso::reale() { return re; } 
   double complesso::image() { return im; } 
   complesso complesso::operator+(complesso x) { complesso z; z.re=re+x.re; z.im=im+x.im; }
   complesso complesso::operator+(double d) { complesso z; z.re=re+d; z.im=im; }

// complio.h
ostream& operator<<(ostream& os, complesso z);
istream& operator>>(istream& is, complesso& z);

//complio.cpp
#include complio.h
#include compl.h
#include <fstream>
...
ostream& operator<<(ostream& os, complesso z)
{
   os << '(' << z.reale() << ',' << z.imag() <<')';
   return os;
}

Mi chiedo innanzitutto se la libreria da includere sia iostream anziché fstream, se non sbaglio fstream non dovrebbe essere utilizzata per le classi ofstream, ifstream, fstream ?
Poi ciò che non comprendo è la definizione dell'operatore << dove
ostream& operator<<(ostream& os, complesso z);

Come devo interpretarla? Mi è chiaro che devo scrivere un numero complesso nel formato tra parentesi tonde, separando parte immaginaria e reale con una virgola ma non capisco i due parametri cosa rappresentino e come si interpretino.
ostream dovrebbe essere lo stream di output e mi torna come valore di ritorno della funzione in quanto alla fine la funzione deve affacciare qualcosa a video, ma non capisco perchè il primo parametro è una classe.
Grazie

12 Risposte

  • Re: Classi e operatori

    La gerarchia di I/O è qui:
    http://en.cppreference.com/w/cpp/i
    Un reference di tipo ostream, accetta istanze di oggetti ostream (cout) o istanze di oggetti derivati da ostream; quindi file (ofstream), istanze di ostringstream e istanze di classi personalizzate purché derivate da ostream.
    Questo vuoi dire che
    
    ostream& operator<<(ostream& os, complesso z)
    
    accetta tutte e tre le seguenti chiamate:
    
    cout << zcomplesso << endl; // stampa a video
    
    file outfile("nomefile");
    outfile << zcomplesso << endl; // scrive su file 
    
    ostringstream oss;
    oss << zcomplesso << endl; // mette in memoria.
    
    ma non capisco perchè il primo parametro è una classe.
    Che ti aspettavi di preciso?
  • Re: Classi e operatori

    Se facciamo un esempio forse riesco a capire meglio.
    Nel caso avessi i valori corrispondenti ai dati della classe come re=2 e im=5.6 quali sarebbero i parametri corrispondenti passati alla funzione operator<< ?
    Perchè a questa funzione viene passato il riferimento di ostream (ostream&) e non semplicemente ostream?
    Poi non comprendo perchè vengono passati due argomenti anziché soltanto la classe, in generale avevo letto che se l'operatore è binario viene passato normalmente un argomento visto che l'istanza della classe è già implicita, mentre se nel caso fosse unario nessun argomento.
  • Re: Classi e operatori

    Perchè a questa funzione viene passato il riferimento di ostream (ostream&) e non semplicemente ostream?
    ostream non è copiabile, quindi occorre passare un reference. Tra l'altro in
    
    ostream& operator<<(ostream& os, complesso z)
    
    stai passando complesso per valore, cosa non sbagliata ma se passi un const reference
    
    ostream& operator<<(ostream& os, const complesso& z)
    

    risparmi una copia nello stack della funzione, che in caso di oggetti "grossi" è poco efficente.
    Nel caso avessi i valori corrispondenti ai dati della classe come re=2 e im=5.6 quali sarebbero i parametri corrispondenti passati alla funzione operator<< ?
    Continuo a non capire che dubbi hai. In questo codice
    
    ostream& operator<<(ostream& os, complesso z)
    {
       os << '(' << z.reale() << ',' << z.imag() <<')';
       return os;
    }
    
    il compilatore vede che esiste un operator<< che accetta un oggetto di tipo complesso(e di cui non ha la minima idea di come è fatto).
    Nel codice della funzione vede che deve inviare un char, un double, un char, un double, un char a un oggetto di tipo ostream per cui invoca gli appositi overload di ostream per i tipi semplici. Restituisce l'oggetto ostream. Fine.
    
    Poi non comprendo perchè vengono passati due argomenti anziché soltanto la classe, in generale avevo letto che se l'operatore è binario viene passato normalmente un argomento visto che l'istanza della classe è già implicita, mentre se nel caso fosse unario nessun argomento.
    
    Dipende se vuoi l'operatore definito a livello di classe o a livello globale.
    http://en.cppreference.com/w/cpp/language/operator_arithmetic
    Alle volte è conveniente definirli a livello di classe, alle volte è conveniente definirli a livello globale, altre volte è conveniente definirli in entrambi i modi. Io, per me, se dovessi scrivere una classe complesso, non definirei operator+ a livello di classe, ma solo a livello globale.
  • Re: Classi e operatori

    shodan ha scritto:


    Dipende se vuoi l'operatore definito a livello di classe o a livello globale.
    http://en.cppreference.com/w/cpp/langu ... arithmetic
    Alle volte è conveniente definirli a livello di classe, alle volte è conveniente definirli a livello globale, altre volte è conveniente definirli in entrambi i modi. Io, per me, se dovessi scrivere una classe complesso, non definirei operator+ a livello di classe, ma solo a livello globale
    Io mi riferivo sempre alla funzione operator<<

    Per capire i miei dubbi proviamo a seguire il mio ragionamento ed analizzare dove cado in errore:
    sempre in merito alla suddetta funzione, supponiamo di istanziare un oggetto classe di tipo complesso con questa istruzione:
    complesso dato(2,5.6);
    Quindi mi trovo i dati della classe con re=2 e im=5.6.

    Se poi eseguo:
    cout << dato;
    il compilatore sa che deve essere richiamata la funzione operator<<
    dove a sinistra dell'operatore ho giustamente cout che rappresenta l'istanza della classe 'ostream' e giustamente trovo corrispondenza come valore di ritorno in tale funzione operator<<
    mentre a destra dell'operatore ho soltanto l'oggetto dato mentre la mia funzione operator<< ha in ingresso 2 parametri.
    Mi verrebbe da dire che viene passato solo l'istanza "dato" ma l'altro parametro ostream& os dove è finito come argomento attuale?
    E' forse in qualche modo implicito?
  • Re: Classi e operatori

    Scrivere
    
    cout << dato
    
    equivale a
    
    cout = operator<<(cout, dato);
    
    mentre se passi, che so, un double scrivere
    
    cout << 2.5
    
    equivale a
    
    cout = cout.operator<<(2.5);
    
    Chiariti i dubbi?
  • Re: Classi e operatori

    Non dovrebbe essere semplicemente
    cout << 2.5
    equivale a
    cout.operator<<(2.5);
    ? Senza l'uguale intendo?
  • Re: Classi e operatori

    Si, è sufficiente. Però così si esplicita il fatto che venga restituita la stessa cosa che viene passata come parametro (la cosa è puramente didattica).
  • Re: Classi e operatori

    shodan ha scritto:


    Scrivere
    
    cout << dato
    
    equivale a
    
    cout = operator<<(cout, dato);
    
    Ora le cose si fanno molto più chiare
    mentre se passi, che so, un double scrivere
    
    cout << 2.5
    
    equivale a
    
    cout = cout.operator<<(2.5);
    
    Correggetemi se sbaglio: nel primo caso dovrebbe essere invocata la funzione operator<< che ho ridefinito nella classe mentre nel secondo caso l'operatore standard di flusso << poiché il parametro passato è un double e quindi il compilatore dovrebbe interpretare che la funzione che "calza appropriatamente" non è quella definita nella classe ma quella associata all'operatore standard <<.
  • Re: Classi e operatori

    Ni, nel senso che hai definito un overload di operator<< per la tua classe, ma tale funzione non appartiene alla tua classe (non è definita nel corpo della stessa).
  • Re: Classi e operatori

    shodan ha scritto:


    Ni, nel senso che hai definito un overload di operator<< per la tua classe, ma tale funzione non appartiene alla tua classe (non è definita nel corpo della stessa).
    E' vero non appartiene alla mia classe, ma allora in tal caso quale logica segue il compilatore se come parametro viene passato un double anziché un oggetto classe complesso ?
    Restituisce un errore?! Come verrebbe gestita questa situazione nel mio caso?
    Grazie per i chiarimenti più che esaustivi precedenti
  • Re: Classi e operatori

    Il compilatore cerca un overload di operator<< in grado di ricevere un double, se non lo trova da errore.
    Tuttavia ogni classe derivata da std::ostream (più precisamente da std::basic_ostream) ha un set di operator<<, definiti a livello di classe, che vengono invocati quando viene inviato un tipo fondamentale (un char, un int etc.), pertanto (a meno di castronate cercate apposta) il problema sui tipi fondamentali non esiste.
  • Re: Classi e operatori

    Ok, giusto per concludere... il mio dubbio derivava dal fatto di non aver chiaro che la chiamata alla funzione operatore si potesse leggere nel modo che hai indicato, cioè :
    cout = operator<<(cout, dato);
    
    Questo mi ha aperto un mondo, non lo sapevo e non avrei mai saputo come arrivarci
Devi accedere o registrarti per scrivere nel forum
12 risposte