class Rettangolo { public: double area (); private: double altezza; double larghezza; }; |
Nel capitolo dedicato alle strutture abbiamo già utilizzato un oggetto
Rettangolo
del tutto simile a quello appena dichiarato; la
differenza è che, come si vede, i campi altezza
e
larghezza
sono stati dichiarati private: essi non
sono accessibili da nessuna funzione che non sia una contenuta nella
classe Rettangolo
. Al contrario, la funzione area
è
stata dichiarata public proprio perché studiata per essere
acceduta (tramite gli operatori `.'
e `->'
, come per le
strutture) dal mondo esterno. Dunque, la larghezza e l'altezza di un
oggetto di tipo Rettangolo
non possono essere modificate in
maniera semplice: abbiamo bisogno di introdurre una funzione la quale
ci consenta tale operazione, come vediamo nel seguente esempio:
// ex9_2_1.cpp class Rettangolo { public: double area () { // definizione return larghezza * altezza; } void modifica (double, double); // dichiarazione private: double altezza, larghezza; }; void Rettangolo::modifica (double Altezza, double Larghezza) { altezza = Altezza; larghezza = Larghezza; } void main() { Rettangolo r; r.modifica (5, 7); r.area(); // = 35 // r.altezza = 10; // NO! altezza e` PRIVATO }
Una prima importante osservazione: i campi di una classe possono essere
variabili (primite o derivate), funzioni, textbf enum,
strutture, altre classi (sconsigliato); in ogni caso valgono le regole
della protezione dei membri: si tratti di una funzione o una variabile,
dall'esterno ci si può accedere solo se essa è public. Per
quanto riguarda le funzione, esse possono essere definite all'interno
della classe 9.1 oppure semplicemente dichiarate e
definite altrove; per definire una funzione già dichiarata all'interno
di una classe, dobbiamo utilizzare il nuovo operatore ``risolutore di
visibilità'': `::'
il quale accetta come operando sinistro il
nome della classe che contiene la dichiarazione di funzione, e come
operando destro il nome che la funzione stessa ha all'interno della
classe. Un'ultima nota: i membri di una classe che non sono preceduti da
nessuna dichiarazione di protezione sono implicitamente
private. Vediamo un altro semplice esempio:
// ex9_2_2.cpp #include <iostream.h> const int MAX = 100; class Voti { double voti[MAX]; // campo PRIVATO int n; // numero dei voti inseriti public: // funzione di inserimento che torna `true' // in caso di successo, `false' in caso di fallimento bool inserisci (double v); // funzione che inizializza i campi dati privati // dal prossimo capitolo sara` sostituita // dalle funzioni costruttori void inizializza (); // funzioni che tornano la media dei voti, // il massimo e il minimo di essi double media (); double max (); double min (); }; bool Voti::inserisci (double v) { if (n >= MAX || v < 0) return false; voti[n++] = v; return true; } void Voti::inizializza () { for (int i = 0; i < MAX; i++) voti[i] = 0; n = 0; } double Voti::media () { if (n == 0) return -1; // errore: nessun dato! double somma = 0; for (int i = 0; i < n; i++) somma += voti[i]; return somma / n; } double Voti::max () { if (n == 0) return -1; // errore: nessun dato! double m = voti[0]; for (int i = 1; i < n; i++) if (voti[i] > m) m = voti[i]; return m; } double Voti::min () { if (n == 0) return -1; // errore: nessun dato! double m = voti[0]; for (int i = 1; i < n; i++) if (voti[i] < m) m = voti[i]; return m; } void main() { Voti v; v.inizializza (); cout << "scrivi un numero negativo per terminare\n"; for (int i = 0; i < MAX; i++) { double voto; cout << "? "; cin >> voto; if (voto < 0) break; v.inserisci (voto); } cout << "media : " << v.media() << "\n"; cout << "massimo : " << v.max() << "\n"; cout << "minimo : " << v.min() << "\n"; }
esempio di output:
scrivi un numero negativo per terminare
? 7
? 7.5
? 6
? 6
? 7
? -1
media : 6.7
massimo : 7.5
minimo : 6
Come si vede, una volta costruita, la classe Voti
è semplicissima da
utilizzare, molto più di un semplice array, per il quale dovremmo di volta
in volta controllare gli indici e passare il numero degli elementi ad ogni
funzione. Non solo: nel prossimo capitolo vedremo che è possibile cambiare
la struttura dati interna, che in questo caso è costituita da un array non
dinamico, senza modificare il programma principale; in particolare,
utilizzeremo un array dinamico.
Ovviamente, come vediamo nel seguente esempio, anche gli oggetti classe possono essere allocati dinamicamente.
// ex9_2_3.cpp #include <iostream.h> const double RADICE2 = 1.4142135624; class Quadrato { public: void impostaDiag (double); void impostaLato (double); double lato () { return l; } double diag () { return d; } double area () { return l*l; } private: double l; double d; }; void Quadrato::impostaDiag (double x) { d = x; l = x / RADICE2; } void Quadrato::impostaLato (double x) { l = x; d = x * RADICE2; } void stampa (const Quadrato* q) { cout << "diagonale: " << q->diag() << "\n"; cout << "lato: " << q->lato() << "\n"; cout << "area: " << q->area() << "\n"; } void main() { Quadrato* q = new Quadrato; double x; cout << "diagonale? "; cin >> x; q->impostaDiag (x); stampa (q); cout << "lato? "; cin >> x; q->impostaLato (x); stampa (q); }
esempio di output:
diagonale? 6
diagonale: 6
lato: 4.24264
area: 18
lato? 4.5
diagonale: 6.36396
lato: 4.5
area: 20.25
Il vantaggio di avere a nostra disposizione una classe Quadrato
consiste, ad esempio, nel fatto che possiamo inserire il lato oppure la
diagonale, facendo in modo che il programmi imposti opportunamente la
dimensione non data. Importante: è probabile che chi utilizza la classe
Quadrato
abbia intenzione di accedere le lunghezze di lato e diagonale;
tuttavia non bisogna dichiarare tali informazioni public,
bensì creare delle funzioni di accesso (nel nostro esempio:
impostaDiag
, impostaLato
, lato
e diag
) le quali
permettano all'utilizzatore della classe di avere accesso alla struttura dati,
mantenendo quest'ultima protetta e coerente. Cosa succederebbe infatti se
avessimo la seguente classe Quadrato2
in vece di Quadrato
?
class Quadrato2 { public: area (); double lato; double diag; };
L'utilizzatore potrebbe impostare la lunghezza del lato ma non quella della diagonale (o viceversa) per cui ci troveremmo con un dato incoerente e, per questo, inutilizzabile. Uno dei vantaggi delle classi in C++ è proprio il seguente: esse ``forzano'' il programmatore ad utilizzare strutture dati solide eppure flessibili, dando spazio in futuro ad aggiornamenti, modifiche e nuovi utilizzi.
Libro
contentente i seguenti campi dati:
nome del libro (array di caratteri non dinamico), costo, numero di scaffale;
si abbiano inoltre le seguenti funzioni membro:
void inizializza (const char*, int, int);
void stampa (); void applicaSconto (); |