Sovraccarico degli operatori: funzione membro e funzione non membro?


121

Ho letto che un operatore sovraccarico dichiarato come funzione membro è asimmetrico perché può avere un solo parametro e l'altro parametro passato automaticamente è il thispuntatore. Quindi non esiste uno standard per confrontarli. D'altra parte, l'operatore sovraccarico dichiarato come a friendè simmetrico perché passiamo due argomenti dello stesso tipo e quindi possono essere confrontati.

La mia domanda è che quando posso ancora confrontare il valore di un puntatore con un riferimento, perché sono preferiti gli amici? (l'utilizzo di una versione asimmetrica fornisce gli stessi risultati di simmetrica) Perché gli algoritmi STL utilizzano solo versioni simmetriche?


11
La tua domanda riguarda solo gli operatori binari. Non tutti gli operatori sovraccaricati sono limitati a un singolo parametro. L'operatore () può accettare qualsiasi numero di parametri. Gli operatori unari, d'altra parte, non possono avere parametri.
Charles Salvia


4
Questo è uno dei tanti argomenti trattati nelle FAQ di C ++: Sovraccarico degli operatori
Ben Voigt

Risposte:


148

Se definisci la funzione di overload dell'operatore come funzione membro, il compilatore traduce espressioni come s1 + s2in s1.operator+(s2). Ciò significa che la funzione membro di overload dell'operatore viene richiamata sul primo operando. Ecco come funzionano le funzioni membro!

Ma cosa succede se il primo operando non è una classe? C'è un grosso problema se vogliamo sovraccaricare un operatore in cui il primo operando non è un tipo di classe, piuttosto diciamo double. Quindi non puoi scrivere in questo modo 10.0 + s2. Tuttavia, è possibile scrivere funzioni membro con overload dell'operatore per espressioni come s1 + 10.0.

Per risolvere questo problema di ordinamento , definiamo la funzione di sovraccarico dell'operatore come friendSE deve accedere ai privatemembri. Fallo friendSOLO quando ha bisogno di accedere a membri privati. Altrimenti, rendilo semplicemente funzione non membro non amico per migliorare l' incapsulamento!

class Sample
{
 public:
    Sample operator + (const Sample& op2); //works with s1 + s2
    Sample operator + (double op2); //works with s1 + 10.0

   //Make it `friend` only when it needs to access private members. 
   //Otherwise simply make it **non-friend non-member** function.
    friend Sample operator + (double op1, const Sample& op2); //works with 10.0 + s2
}

Leggi queste:
Un piccolo problema di ordinamento negli operandi
Come le funzioni non membro migliorano l'incapsulamento


2
"Fallo friendsolo quando ha bisogno di accedere a membri privati ​​.. e quando non hai / sei stanco di scrivere
funzioni

4
@ Abhi: scegli la tua scelta: incapsulamento migliorato vs abitudine di scrittura pigra!
Nawaz

6
@ matthias, non tutti gli operatori sono commutativi. Un semplice esempio è a/b.
edA-qa mort-ora-y

3
Un modo comune per evitare di richiedere friendagli operatori non membri è di implementarli in termini di operatori di assegnazione delle operazioni (che saranno quasi certamente membri pubblici). Ad esempio, è possibile definire T T::operator+=(const T &rhs)come membro e quindi definire non membro T operator(T lhs, const T &rhs)come return lhs += rhs;. La funzione non membro deve essere definita nello stesso spazio dei nomi della classe.
Adrian McCarthy

2
@ricky: Ma se lhs è una copia (come è nel mio commento), allora il fatto che lhs cambi non ha importanza.
Adrian McCarthy

20

Non è necessariamente una distinzione tra friendsovraccarichi di operatori e sovraccarichi di operatori di funzione membro, poiché è tra sovraccarichi di operatori globali e sovraccarichi di operatori di funzione membro.

Un motivo per preferire un sovraccarico di operatori globali è se si desidera consentire espressioni in cui il tipo di classe appare sul lato destro di un operatore binario. Per esempio:

Foo f = 100;
int x = 10;
cout << x + f;

Funziona solo se è presente un sovraccarico dell'operatore globale per

Operatore Foo + (int x, const Foo & f);

Si noti che il sovraccarico dell'operatore globale non deve necessariamente essere una friendfunzione. Ciò è necessario solo se ha bisogno dell'accesso ai membri privati ​​di Foo, ma non è sempre così.

Indipendentemente da ciò, se Foosolo avesse un sovraccarico dell'operatore della funzione membro, come:

class Foo
{
  ...
  Foo operator + (int x);
  ...
};

... allora potremmo avere solo espressioni in cui Fooun'istanza appare a sinistra dell'operatore più.


3
+1 per fare la distinzione tra funzioni membro e funzioni non membro piuttosto che funzioni membro e amico. Immagino che oggi diremmo "ambito globale o spazio dei nomi".
Adrian McCarthy
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.