C ++ ad un amico o non ad un amico


19

Ho una programmazione orientata agli oggetti con corso c ++ questo semestre al college e stavamo imparando le funzioni degli amici.

Istintivamente non mi piacciono per la loro capacità di eludere la sicurezza offerta dall'incapsulamento e dal nascondimento dei dati, ho letto alcuni articoli su Internet e alcune persone hanno pensato che fosse una buona idea con alcuni usi legittimi.

Cosa direbbe un esperto di OOP sulle fucntions degli amici in C ++? Dovrei semplicemente passarci sopra o dovrei saperne di più?


@Tutti: risposte e commenti fantastici, questo è un ottimo modo per imparare, non avrei mai imparato a conoscere gli amici in questi dettagli in un libro di testo.
nikhil,

Risposte:


13

Non è sempre conveniente rendere tutte le funzioni correlate ai membri di una classe C ++ di quella classe. Ad esempio, immagina un'implementazione dell'algebra vettoriale con moltiplicazione scalare. Vogliamo scrivere:

 double a;
 Vector v, w;
 w = v * a;

Possiamo farlo con una funzione membro:

public class Vector {
 ...
 Vector operator*(double a);
}

Ma vorremmo anche scrivere:

w = a * v

Ciò richiede una funzione gratuita:

 Vector operator*(double a, Vector v)

La friendparola chiave è stata aggiunta a C ++ per supportare questo utilizzo. La funzione gratuita fa parte dell'implementazione della classe Vector e deve essere dichiarata nella stessa intestazione e implementata nello stesso file di origine.

Allo stesso modo possiamo usare friendper semplificare l'implementazione di classi strettamente accoppiate, come una raccolta e un iteratore. Ancora una volta, dichiarerei entrambe le classi nella stessa intestazione e le implementerei nello stesso file di origine.


3
"Questo richiede una funzione gratuita". No, non è così: inline Vector operator*(double a, Vector v) { return v*a; }. Soluzione canonica in effetti.
MSalters,

1
@MSalters: buon punto. Ho scelto un cattivo esempio. Penso che la tua funzione inline sia una funzione gratuita per definizione, ma non è richiesta alcuna dichiarazione di amicizia.
Kevin Cline,

4
@MSalters: è valido solo se * è commutativo rispetto a a e v (x). Se i componenti vettoriali sono generici (non necessariamente scalari) devi mantenere l'ordine degli operandi
Emilio Garavaglia,

È piuttosto teorico. Forse l'unico caso non commutativo comune sarebbe inline Vector operator*(double a, Vector v) { return -v*a; }e ciò non richiede ancora amicizia.
Salterio il

16

Le funzioni degli amici non sono diverse dalle funzioni dei membri in termini di incapsulamento. Possono, tuttavia, offrire altri vantaggi, ad esempio essere più generici, soprattutto per quanto riguarda i modelli. Inoltre, alcuni operatori possono essere specificati solo come funzioni gratuite, quindi se si desidera che abbiano accesso ai membri, è necessario friend.

È meglio per frienduna singola funzione che essere costretti a fare qualcosa che non vuoi essere pubblico. Ciò significa che tutto il mondo può usarlo, invece di una sola funzione.


+1 per le funzioni Amico non differiscono dalle funzioni membro in termini di incapsulamento. Questo è vero solo per le funzioni dei membri pubblici.
TheFogger

1
@TheFogger: Probabilmente, potresti anche frienduna funzione che è anche "privata", come dichiarata solo in una singola TU.
DeadMG

5

Se sei appassionato di quello che fai, impareresti tutto sul C ++. Scopri a cosa servono, come usarli e poi - e solo allora - decide di non usarli. Per lo meno, sarai preparato quando leggi il codice di qualcun altro che utilizza questo aspetto del C ++.


5

" Cosa direbbe un esperto di OOP ... " Dipende principalmente da quanto sia esperto in C ++, che - secondo le sue specifiche - non è (e non vuole essere) un linguaggio per puristi.

OOP Gli zeloti non usano C ++ (preferiscono Smalltalk e come Java).

Gli zelot di programmazione funzionale non usano C ++ (preferiscono LISP e i suoi successori)

Alla maggior parte degli esperti di OOP non piace la funzione di amico semplicemente perché vogliono che la parte OOP di C ++ si comporti come Smalltalk. Ma C ++ non è Smalltalk e non riescono nemmeno a capire che l' amico non rompe l'incapsulamento , per la ragione molto semplice che una funzione non può essere amica della tua classe senza che la tua classe la voglia .

E dal punto di vista della "funzionalità", tra a.fn(b)e fn(a,b)non c'è differenza (dov'è fnun amico): le parti coinvolte sono le stesse. Semplicemente, una sintassi può essere più adatta di un'altra: se fn è commutativo rispetto a , ae probabilmente è più adatto allora (dove un aspetto ha un "ruolo speciale" che, in realtà, non lo fa).bfn(a,b)a.fn(b)


1
Gli "zeloti OOP" a cui piace Java non hanno capito OOP. Getters? Setter? Nessuna sintassi semplice per chiusure? Per parafrasare Alan Kay, non è così che immaginava OOP.
Konrad Rudolph,

@Konrad: i fanatici sono un set superiormente illimitato. C'è sempre uno zelota più zelota di un certo zelota.
Emilio Garavaglia,

Devo dire che ho votato perché mi è piaciuto molto l'ultimo paragrafo. Ha molto senso.
julealgon

5

L '"amico" viola l'incapsulamento?

No non lo fa. "Friend" è un meccanismo esplicito per la concessione dell'accesso, proprio come l'appartenenza. Non puoi (in un programma conforme standard) concederti l'accesso a una classe senza modificarne l'origine.


2

Le domande frequenti su C ++ sono sintetiche:

Usa un membro quando puoi e un amico quando devi.

Le FAQ presentano uno dei modi più utili di pensare all'amicizia:

Molte persone pensano che un amico funzioni come qualcosa al di fuori della classe. Invece, prova a pensare alla funzione di un amico come parte dell'interfaccia pubblica della classe. Una funzione di amico nella dichiarazione di classe non viola l'incapsulamento più di quanto una funzione di membro pubblico violi l'incapsulamento: entrambi hanno esattamente la stessa autorità per quanto riguarda l'accesso alle parti non pubbliche della classe.

Forse l'uso più comune delle funzioni degli amici è il sovraccarico di << per I / O.


0

Le funzioni degli amici sono utilizzate al meglio per le definizioni degli operatori definite dall'utente. Sono utili in altre situazioni, ma se ti ritrovi a specificare frequentemente le classi di amici, potresti essere in una deviazione di progettazione (solo un buon autocontrollo da usare durante la scrittura del codice).

Fai attenzione alla dichiarazione di "sicurezza" nella domanda originale. I modificatori di accesso sono lì per impedirti di scrivere codice errato in caso di incidente, proprio come il compilatore in un certo senso. I modificatori di accesso limitano l'interfaccia e servono a comunicare quali funzioni sono importanti per utilizzare la classe (pubblica e protetta) e quali sono state create per rendere la classe più carina per i manutentori (privata). I modificatori non costituiscono sicurezza in quanto esistono molti modi per accedere ai dati privati. Ad esempio, ottieni un puntatore alla classe e alle sue dimensioni e vai a pescare.


-2

Le funzioni di amicizia C ++ sono strettamente correlate alle seguenti funzionalità:

  1. funzioni gratuite
  2. funzioni statiche
  3. funzioni amico

Ciò significa che non hanno questo puntatore e quindi sono al di fuori della classe / oggetto. D'altra parte, spesso accettano parametri che li rendono nuovamente appartenenti alla classe. Ecco qualche esempio che chiarisce il link:

class B;
class A {
public:
    friend void f(A &a, B &b);
private:
    int m_a;
};
class B {
public:
   friend void f(A &a, B &b);
private:
   int m_b;
};
void f(A &a, B &b) { /* uses both A's and B's private data */ }

L'unica differenza tra le funzioni statiche e le funzioni di amico è che una funzione di amico può usare diverse classi.

L'uso del meccanismo amico in c ++ richiede programmatori che hanno circa 10-15 anni di esperienza con il modo di programmazione c ++, e quindi inizialmente dovresti evitarlo. È funzionalità avanzata.


7
E tu hai derivato 10-15 anni come?
DeadMG

10-15 anni provengono dal momento in cui per la prima volta diventano effettivamente necessari.
tp1

3
Quindi hai arbitrariamente inventato un numero, quindi.
DeadMG

3
-1: "Dovresti evitarlo." Ogni funzionalità di C ++ è stata creata per risolvere un problema. Quando si presenta questo problema, utilizzare la funzionalità appropriata.
Kevin Cline,

Grazie per il -1. C'era una ragione per quel commento. Immagino sia difficile pensare che non tutte le funzionalità siano adatte ai principianti.
tp1,
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.