Classi
Ragazzi ho una domanda sulle classi in C++...
Perchè non scriviamo friend anche agli operatori aritmetici??Non accedono anche loro ai membri privati della classe??
es:
class nn{
private:
public:
friend ostream& operator<<();
nn& operator%=(const nn&); -------------------------------->perché non friend nn& operator%=(const nn&);
}
grazie!! XD
Perchè non scriviamo friend anche agli operatori aritmetici??Non accedono anche loro ai membri privati della classe??
es:
class nn{
private:
public:
friend ostream& operator<<();
nn& operator%=(const nn&); -------------------------------->perché non friend nn& operator%=(const nn&);
}
grazie!! XD
Risposte
Non c'è alcuna ragione per cui tu non possa farlo. Ma la tua domanda non avrebbe dovuto essere perché non posso farlo, cosa assolutamente possibile, ma perché l'autore del libro/tutorial/guida/codice non l'ha fatto. Per rispondere a questa seconda domanda dovresti porre la domanda all'autore, oppure dare riferimenti e/o postare il codice della classe completo. Puoi anche avere gli operatori come funzioni libere (non membro) e non non friend a patto di avere abbastanza funzioni membro o avere dati pubblici per poterlo fare.
Scusami ma non ho capito perché non é necessario che ci sia friend??? Sulla possibilità di metterlo comunque sono d'accordo non é mica vietato..
Ti posto il codice di una classe cosi,forse può aiutarti a darmi una mano XD
Ti faccio notare (anche se sicuramente lo saprai),che ci sono due file uno .cpp ed uno punto .h
Ciao
Ti posto il codice di una classe cosi,forse può aiutarti a darmi una mano XD
Ti faccio notare (anche se sicuramente lo saprai),che ci sono due file uno .cpp ed uno punto .h
Ciao
//////////////////////////////file.h #include <iostream> using namespace std; struct posizione { char lettera; int numero; }; class Pavimento { friend ostream& operator<<(ostream&, const Pavimento&); int dim, *mat; int valida(posizione); int& casella(posizione); public: Pavimento(int = 3); int operator!() const; int operator ~() const; };
/////////////////////////////////////////////////////////////file.cpp #include"compito.h" int& Pavimento::casella(posizione s) { // ritorna la casella in posizione s int r = s.lettera - 'A'; int c = s.numero - 1; return *(mat + r * dim + c); } Pavimento::Pavimento(const Pavimento& p) { dim = p.dim; mat = new int [dim * dim]; for (int i = 0; i < dim * dim; i++) mat[i] = p.mat[i]; } int Pavimento::operator!() const { int max = 0, conta, r, c; // r: indice di riga, r = 0, 1, 2, ..., dim - 1 // c: indice di colonna, c = 0, 1, 2, ..., dim - 1 for (r = 0; r < dim; r++) { c = 0; while (c < dim) { while ((c < dim) && !(*(mat + r * dim + c))) c++; conta = 0; while ((c < dim) && (*(mat + r * dim + c) )){ c++; conta++; } max = max < conta ? conta : max; } } for (c = 0; c < dim; c++) { r = 0; while (r < dim) { while ((r < dim) && !(*(mat + r * dim + c) )) r++; conta = 0; while ((r < dim) && (*(mat + r * dim + c))){ r++; conta++; } max = max < conta ? conta : max; } } return max; } int Pavimento::operator~() const { int conta = 0; for (int i = 0; i < dim * dim; i++) if (*(mat + i) ) conta++; return conta; } ostream& operator<<(ostream& os, const Pavimento& p) { for (int i = 0; i < p.dim; i++) { for (int j = 0; j < p.dim; j++) { if(*(p.mat + (p.dim * i) + j)) os << 'O'; else os << '-'; if (j != p.dim - 1) os << '\t'; } if (i != p.dim - 1) os << '\n'; } return os; }
Perché gli operatori sono elementi della classe e quindi possono leggere gli elementi privati della classe. Personalmente trovo che sia meglio usare le classi e le funzioni friends il meno possibile. Se ti trovi ad usarle troppo vuol dire che probabilmente hai sbagliato il design della classe.
I tag di codice, per piacere: rendono tutto molto più leggibile!
ok vict85 grazie, adesso mi torna,comunque friend lo uso solo per gli stream di ingresso e di uscita.
ehm Raptorista , scusami non ho capito come posso migliorare la lettura del codice

ehm Raptorista , scusami non ho capito come posso migliorare la lettura del codice

Ti posto velocemente questo link che tratta argomenti che potresti trovare interessanti*: How Non-Member Functions Improve Encapsulation.
Per quanto riguarda quello che ti ha detto Raptorista, devi inserire il codice tra "[ code ]" e "[ /code ]" (inseriscili nel codice senza le virgolette e senza gli spazi tra le parentesi e la parola interna - ho dovuto scriverli così perché se no non si vedeva niente). Alternativamente puoi usare il pulsante Code nell'editor avanzato. Facendo in questo modo ti inserirà il codice in un box come il seguente:
* ATTENZIONE: Può cambiare il modo di scrivere le proprie classi in C++ per sempre..
Per quanto riguarda quello che ti ha detto Raptorista, devi inserire il codice tra "[ code ]" e "[ /code ]" (inseriscili nel codice senza le virgolette e senza gli spazi tra le parentesi e la parola interna - ho dovuto scriverli così perché se no non si vedeva niente). Alternativamente puoi usare il pulsante Code nell'editor avanzato. Facendo in questo modo ti inserirà il codice in un box come il seguente:
Questo è il mio codice.. Mantengo la formattazione.. E non permetto l'inserimento di emoticons per sbaglio :D :D :D :D :D
* ATTENZIONE: Può cambiare il modo di scrivere le proprie classi in C++ per sempre..

ok grazi per la dritta ,riguardo entrambe le cose!!

"apatriarca":
* ATTENZIONE: Può cambiare il modo di scrivere le proprie classi in C++ per sempre..


scusate ma non capisco perché dovrebbe cambiare il mio modo di programmare ,forse non ho compreso a pieno ciò che c'è scritto nel link che mi hai postato.. 
quello che ho capito é che ,dichiarare le funzioni friend,può essere un vantaggio,perchè aumenta il livello di incapsulamento e di conseguenza la robustezza del codice..

quello che ho capito é che ,dichiarare le funzioni friend,può essere un vantaggio,perchè aumenta il livello di incapsulamento e di conseguenza la robustezza del codice..
Diciamo che quando è uscito nel 2000 è stato abbastanza rivoluzionario, è possibile che adesso lo sia molto meno. Però non dice che devi usare le funzioni friend, queste ultime non sono infatti molto diverse da quelle membro perché in entrambi i casi accedono alla parte privata della classe. Quell'articolo dice principalmente due cose:
1. All'interfaccia pubblica di una classe appartengono sia le funzioni membro che le funzioni non-membro che agiscono su di essa (o ne creano una nuova). Quando è uscito l'articolo era abbastanza diffusa l'idea che
Non so quanto questa opinione sia ancora radicata, ma 11 anni fa l'OOP non si era ancora del tutto sviluppato e c'era ancora molta incomprensione. Questa idea è abbastanza potente e importante. Significa che chiunque può modificare e arricchire l'interfaccia pubblica di una classe senza modificarla a patto di poter implementare la nuova funzionalità con la vecchia interfaccia pubblica. È infatti sufficiente creare una nuova funzione non membro.
2. Se è possibile implementare una funzione sia come funzione membro che come funzione non membro NON friend, allora è meglio implementarla come funzione non membro. Meno funzioni accedono alla parte privata della classe, maggiore sarà infatti l'incapsulamento e maggiore la robustezza del codice. La conseguenza di questa affermazione è che il numero di funzioni membro o funzioni friend devono, a meno di avere buoni motivi, essere ridotti al minimo per avere un maggiore incapsulamento e quindi un codice più robusto.
1. All'interfaccia pubblica di una classe appartengono sia le funzioni membro che le funzioni non-membro che agiscono su di essa (o ne creano una nuova). Quando è uscito l'articolo era abbastanza diffusa l'idea che
being object-oriented means putting functions inside the classes containing the data on which the functions operate.
Non so quanto questa opinione sia ancora radicata, ma 11 anni fa l'OOP non si era ancora del tutto sviluppato e c'era ancora molta incomprensione. Questa idea è abbastanza potente e importante. Significa che chiunque può modificare e arricchire l'interfaccia pubblica di una classe senza modificarla a patto di poter implementare la nuova funzionalità con la vecchia interfaccia pubblica. È infatti sufficiente creare una nuova funzione non membro.
2. Se è possibile implementare una funzione sia come funzione membro che come funzione non membro NON friend, allora è meglio implementarla come funzione non membro. Meno funzioni accedono alla parte privata della classe, maggiore sarà infatti l'incapsulamento e maggiore la robustezza del codice. La conseguenza di questa affermazione è che il numero di funzioni membro o funzioni friend devono, a meno di avere buoni motivi, essere ridotti al minimo per avere un maggiore incapsulamento e quindi un codice più robusto.
ok adesso ho capito,ma solo una cosa non mi é chiara.Se per accedere alla parte privata della classe ho bisogno di funzioni membro o friend,io con le funzioni non membro non friend,che cosa ci faccio??
Non è sempre necessario accedere alla parte privata della classe. Spesso è possibile usare le altre funzioni pubbliche. È per esempio abbastanza comune inserire delle funzioni per accedere a delle "proprietà" della classe (preferisco non parlare di variabili perché non è detto che quello a cui si acceda sia effettivamente una variabile o sia calcolato in qualche modo). Quando una funziona abbia solo bisogno di accedere a queste "proprietà", non ha bisogno di essere una funzione membro o friend perché può usare le funzioni membro pubbliche già presenti nell'interfaccia. Usare funzioni non membro ha comunque anche altri vantaggi al di là dell'incapsulamento. Una funzione non membro può infatti essere usata con le funzioni di algorithm. E se si programma in C++ moderno, quest'ultimo è un enorme vantaggio.
scusami,stiamo parlando di cose che vanno, oltre quello che mi hanno insegnato ,ma le proprietà di cui parli dove vengono definite??
Vediamo qualcosa di meno astratto. Consideriamo un punto nel piano, lo puoi rappresentare sia in coordinate polari che cartesiane. Per cui scriviamo ad esempio:
In questo caso stiamo usando la rappresentazione cartesiana ma nulla ci vieta in teoria di usare quella polare. Supponiamo allora di voler scrivere una funzione che ci restituisce la distanza tra due punti. Abbiamo queste scelte:
1. Scrivere la funzione come funzione membro e accedere direttamente a _x e _y.
2. Scrivere la funzione come funzione non membro friend e accedere direttamente a _x e _y.
2. Scrivere la funzione come funzione membro e usare le funzioni x() e y().
3. Scrivere la funzione come funzione non membro e usare le funzioni x() e y().
Quell'articolo dice che 3 è preferibile. In teoria sia 2 che 3 offrono lo stesso incapsulamento (la funzione non cambia se cambia l'implementazione interna della classe da cartesiana a polare), ma non c'è ragione di avere la funzione come membro della classe se non accede alla parte privata. Le funzioni non membro offrono infatti una maggiore flessibilità (possono essere usati in molti posti dove le funzioni membro non possono essere usate. Seguendo questa regola scriveremmo allora la nostra funzione come:
Lo stesso discorso vale se ad esempio vogliamo implementare una funzione che ci restituisca la differenza di angolo tra due punti. L'implementazione più semplice usa ovviamente angle() e questa funzione avrà performance diverse a seconda dell'implementazione scelta. Nota che anche radius e angle avrei potuto implementarla a partire da x() e y(), ma in questo caso ho deciso che renderle funzioni membro perché rendendole funzioni non membro non avrei potuto cambiare l'implementazione in modo da usare la rappresentazione polare. Come per ogni regola, esistono quindi delle eccezioni ed è fondamentale usare sempre la propria testa.
struct Point { Point(double x, double y) : _x(x), _y(y) {} Point(const Point& p) : _x(p._x), _y(p._y) {} double x() const { return _x; } Point& x(double x) { _x = x; return *this; } double y() const { return _y; } Point& y(double y) { _y = y; return *this; } double radius() const { return sqrt(_x*_x + _y*_y); } Point& radius(double radius) { double s = radius / this->radius(); _x *= s; _y *= s; return *this; } double angle() const { return atan2(_y, _x); } Point& angle(double angle) { double r = this->radius(); double c = cos(angle); double s = sin(angle); _x = r * c; _y = r * s; return *this; } private: double _x, _y; };
In questo caso stiamo usando la rappresentazione cartesiana ma nulla ci vieta in teoria di usare quella polare. Supponiamo allora di voler scrivere una funzione che ci restituisce la distanza tra due punti. Abbiamo queste scelte:
1. Scrivere la funzione come funzione membro e accedere direttamente a _x e _y.
2. Scrivere la funzione come funzione non membro friend e accedere direttamente a _x e _y.
2. Scrivere la funzione come funzione membro e usare le funzioni x() e y().
3. Scrivere la funzione come funzione non membro e usare le funzioni x() e y().
Quell'articolo dice che 3 è preferibile. In teoria sia 2 che 3 offrono lo stesso incapsulamento (la funzione non cambia se cambia l'implementazione interna della classe da cartesiana a polare), ma non c'è ragione di avere la funzione come membro della classe se non accede alla parte privata. Le funzioni non membro offrono infatti una maggiore flessibilità (possono essere usati in molti posti dove le funzioni membro non possono essere usate. Seguendo questa regola scriveremmo allora la nostra funzione come:
double distance(const Point& p1, const Point& p2) { const double dx = p2.x() - p1.x(); const double dy = p2.y() - p1.y(); return sqrt(dx*dx + dy*dy); }
Lo stesso discorso vale se ad esempio vogliamo implementare una funzione che ci restituisca la differenza di angolo tra due punti. L'implementazione più semplice usa ovviamente angle() e questa funzione avrà performance diverse a seconda dell'implementazione scelta. Nota che anche radius e angle avrei potuto implementarla a partire da x() e y(), ma in questo caso ho deciso che renderle funzioni membro perché rendendole funzioni non membro non avrei potuto cambiare l'implementazione in modo da usare la rappresentazione polare. Come per ogni regola, esistono quindi delle eccezioni ed è fondamentale usare sempre la propria testa.
ok perfetto,ti ringrazio per la tua pazienza ,che mi ha permesso di chiarire alcuni dubbi!!
