2012-03-21 8 views
10

Sto tentando di sovraccaricare operator<< come funzione membro. Funziona se semplicemente fare questo:Impossibile sovraccaricare l'operatore << come funzione membro

friend ostream& operator<<(ostream& os, const MyClass& myClass); nel mio file di intestazione e nel mio file MyClass.cc:

ostream& operator<<(ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

Tuttavia, se provo a prendere il friend fuori e lo rendono una funzione membro, allora si lamenta che operator<< può prendere solo un argomento. Perché?

ostream& MyClass::operator<<(ostream& os, const MyClass& myClass) 
{ 
    return myClass.print(os); 
} 

ho letto in this question che non può essere una funzione membro, ma non so perché?

risposta

35

Se sovraccaricato come funzione membro, a << b viene interpretato come a.operator<<(b), pertanto è necessario un solo parametro esplicito (con this come parametro nascosto).

Poiché ciò richiede che il sovraccarico faccia parte della classe utilizzata come operando di sinistra, non è utile con le normali ostream e così via. Richiederebbe che il sovraccarico faccia parte della classe ostream, non parte della tua classe. Dato che non sei autorizzato a modificare ostream, non puoi farlo. Ciò lascia solo il sovraccarico globale come alternativa.

V'è, tuttavia, un modello piuttosto diffuso in cui si sovraccaricare l'operatore a livello globale, ma hanno che chiamano una funzione membro:

class whatever { 
    // make this public, or the global overload a friend. 
    std::ostream &write(std::ostream &dest) const { 
     // write self to dest 
    } 
}; 

std::ostream &operator<<(std::ostream &os, whatever const &w) { 
    return w.write(os); 
} 

Ciò è particolarmente utile quando/se si desidera un comportamento polimorfico. Non è possibile rendere l'operatore sovraccaricato polimorfico stesso, ma si effettua la funzione membro che chiama virtual, quindi agisce comunque polimorfico.

Modifica: per (spero) chiarire la situazione, è possibile farlo in modi diversi.Il primo e probabilmente il più ovvio è semplicemente rendere pubblico il nostro membro write e farlo chiamare dall'operatore globale. Dal momento che è pubblico, non abbiamo a che fare nulla di speciale per lasciare che l'operatore usa:

class myClass { 
public: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 
}; 

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
    // since `write` is public, we can call it without any problem. 
    return m.write(os); 
} 

Una seconda alternativa è quella di rendere write privato, e dichiarare operator<< un amico per dargli l'accesso:

class myClass { 
    // Note this is private: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 

    // since `write` is private, we declare `operator<<` a friend to give it access: 
    friend std::ostream &operator<<(std::ostream &, myClass const &); 
}; 

std::ostream &operator<<(std::ostream &os, myClas const &m) { 
    return m.write(os); 
} 

C'è una terza possibilità che è quasi come il secondo:

class myClass { 
    // Note this is private: 
    std::ostream &write(std::ostream &os) const { 
     // write stuff to stream 
     return os; 
    } 

    // since `write` is private, we declare `operator<<` a friend to give it access. 
    // We also implement it right here inside the class definition though: 
    friend std::ostream &operator<<(std::ostream &os, myClas const &m) { 
     return m.write(os); 
    } 
}; 

Questo terzo caso usa un po 'strano (e poco conosciuto) regola in C + + chiamato "nome iniezione". Il compilatore sa che una funzione friend non può far parte della classe, quindi, invece di definire una funzione membro, questo "inietta" il nome di tale funzione nell'ambito circostante (l'ambito globale, in questo caso). Anche se operator<< è definito all'interno della definizione di classe, è non una funzione membro a tutti - è una funzione globale.

+0

Cosa succede se l'operatore << 'è dichiarato pubblico nella classe ma non implementato nella classe come' MyClass :: operator << '? È richiesto 'amico'? –

+0

Interessante. Quindi può essere dichiarato nella classe, implementato come funzione non membro e non essere amico della classe per chiamare ancora le funzioni membro pubbliche di quella classe. –

+0

@ 0A0D: Non sono sicuro di seguirlo. Nel mio precedente commento, "it" si riferiva alla funzione membro. Stai parlando di qualcos'altro? –

9

È possibile sovraccaricare operator<< come funzione membro. Ma non puoi scrivere un membro operator<< che porta un ostream sul lato sinistro e la tua classe sul lato destro.

Quando si crea qualcosa una funzione membro (non statica), esiste un primo argomento implicito, l'oggetto chiamante. operator<< è binario, quindi richiede solo 2 argomenti. Se si effettua una funzione membro, è possibile assegnarla solo a un parametro, poiché ne ha già uno (l'oggetto chiamante). E poiché quell'oggetto chiamante è sempre il primo argomento, non è possibile scrivere l'operatore di output come membro (non statico) (almeno, non nella forma standard per quella particolare funzione), perché per quel caso, l'ostream deve essere il primo argomento.

+1

Inoltre, poiché l'implicito 'this' sarà il primo argomento, non è possibile rendere gli operatori ostream come membri della classe - l'ordine degli argomenti sarà a ritroso. –

+0

@JohnZwinck anche se potresti far fare alla tua classe 'myclass >> cout', ma sarebbe strano ... specialmente con il concatenamento. –

+0

Deve essere consultato per accedere al metodo di stampa in MyClass se si tratta di una funzione non membro? –

1

Pensate a come questo: quando si desidera per lo streaming di ostream, si sta chiamando il < < operatore sull'oggetto flusso. E non ti è permesso modificare direttamente il metodo 'privato' dell'ostream. Quindi devi creare una versione sovraccaricata e renderla un amico.

Quando si crea il proprio operatore di < < metodo nella classe, si sta creando un metodo di < < che opererà sulla tua classe, non un oggetto ostream. Immagino che tu possa tenere un ostream internamente alla tua classe e fare il flusso verso di esso, ma avrai difficoltà a scrivere dichiarazioni concatenate come questa: a << b << c.

+0

amico è richiesto anche se il mio metodo di stampa è pubblico in MyClass? –

Problemi correlati