/* Ora vedremo di creare una classe piu' completa, che abbia anche delle * funzioni (metodi), oltre ai dati. * Occorre aver chiaro che gli oggetti si mandano "messaggi" proprio perche' * i loro dati gli appartengono, sono privati, e non possono essere "toccati" * direttamente dagli oggetti esterni. * Questo per il concetto di "information hiding" (nascondere i dati) e * "incapsulation" (unire dati e funzioni in un unica "capsula"). * I dati locali, a cui non si puo' accedere direttamente da un'altro * oggetto, sono modificabili solo tramite gli opportuni metodi, i quali * saranno "pubblici". La regola OOP vuole che i dati di un oggetto possano * essere consultati SOLO tramite l'apposito metodo. * E' possibile definire variabili come globali, ma sconsigliato. * Ecco dunque l'iter che si segue per operare su una variabile privata di * un oggetto: si spedisce il MESSAGGIO relativo a cio' che si chiede, * l'oggetto invoca il METODO che corrisponde a quel messaggio, il quale * cambia il suo stato interno (accedendo alla variabile, ad esempio), ed * eventualmente restituisce dei dati, se richiesti. * Vediamo dunque come si definiscono i metodi in una classe. Si devono * innanzitutto dichiarare i loro prototipi nella parte pubblica: * * class animale { // Definisco la classe animale * float lunghezza; // Questa parte e' PRIVATE di default * int numerozampe; * public: // La parte che segue e' PUBLIC (visibile da tutti) * void mettilungh(float i); // Questi sono i prototipi delle funzioni * void mettizampe(int l); // (o metodi) della nostra classe. * float leggilungh(void); * int leggizampe(void); * }; * * I prototipi sono analoghi a quelli normali: si indicano i parametri * in entrata e uscita. Il codice effettivo delle funzioni si dichiara in * questo modo: * * void animale::mettilungh(float i) * { * lunghezza = i; // Scrive il valore nella variabile privata "lunghezza". * } * * L'unica differenza rispetto alla definizione delle normali funzioni, e' * che occorre aggiungere l'appartenenza ad una classe, precedendo il nome * della funzione con "nomeclasse::". Il segno :: e' detto operatore di * determinazione di visibilita' (scope resolution operator), e in questo * caso dichiara che il metodo mettilungh e' nel campo di visibilita' della * classe animale. In questo modo, e' possibile definire 2 funzioni con lo * stesso nome, ma appartenenti a diverse classi, ad esempio: * * void automezzo::numeroruote(int i) { ... } * void ciclo::numeroruote(int i) { ... } * * Ora instanziamo un automezzo e un ciclo: * * automezzo ferrari; * ciclo graziella; * * E accediamo alle 2 funzioni numeroruote, che non hanno niente a che * vedere tra loro: * * ferrari.numeroruote(4); * graziella.numeroruote(2); * * Vedremo dopo come, grazie al polimorfismo, potremo dare lo stesso nome * a metodi diversi (con diversi parametri I/O) appartenenti anche alla * stessa classe. * Da notare che se un metodo di una classe e' chiamato da un metodo della * stessa classe, non necessita di essere preceduto dall'operatore punto: * dall'esterno della classe si usa ferrari.numeroruote(), dal suo interno * si usa numeroruote() semplicemente. In questo sorgente comunque non ci * sono chiamate tra funzioni all'interno della stessa classe. * Vediamo quindi l'uso di una classe con dati privati e metodi pubblici. */ #include <iostream.h> // Header necessario per usare CIN e COUT // Questo e' un commento C++ ad una sola linea class animale { // Definisco la classe animale float lunghezza; // Questa parte e' PRIVATE di default int numerozampe; public: // La parte che segue e' PUBLIC (visibile da tutti) void mettilungh(float i); // Questi sono i prototipi delle funzioni void mettizampe(int l); // (o metodi) della nostra classe. float leggilungh(void); int leggizampe(void); }; // Ora scriviamo le funzioni (metodi) della classe animale: /*-------------------------------------------------------------------------* * mettilungh: definisce la lunghezza dell'animale. * parametri: in entrata la lunghezza in formato float. *-------------------------------------------------------------------------*/ void animale::mettilungh(float i) { lunghezza = i; // Scrive il valore nella variabile privata "lunghezza". } /*-------------------------------------------------------------------------* * mettizampe: definisce il numero di zampe dell'animale. * parametri: in entrata il num. di zampe in formato int. *-------------------------------------------------------------------------*/ void animale::mettizampe(int l) { numerozampe = l; // Scrive il valore nella variabile privata "numerozampe". } /*-------------------------------------------------------------------------* * leggilungh: legge e restituisce la lunghezza dell'animale. * parametri: in uscita la lunghezza in formato float. *-------------------------------------------------------------------------*/ float animale::leggilungh(void) { return lunghezza; } /*-------------------------------------------------------------------------* * leggizampe: legge e restituisce il num. di zampe dell'animale. * parametri: in uscita il num. di zampe in formato int. *-------------------------------------------------------------------------*/ int animale::leggizampe(void) { return numerozampe; } /////////////////////////////////////////////////////////////////////////// // Il main... /////////////////////////////////////////////////////////////////////////// int main(void) { float temp; // Variabile temporanea che usiamo per le lunghezze. int temp2; // Idem, per il num. zampe. // Ora possiamo dichiarare alcuni oggetti di tipo animale e usarli: animale cane1,cane2,millepiedi; // ecco che istanzio 3 animali... // ossia creo degli esemplari dalla // classe "astratta". // Non possiamo accedere direttamente ai dati come abbiamo fatto prima, // con cane1.numerozampe = 4; eccetera. Ora dobbiamo mandare un messaggio // all'oggetto chiedendogli di modificare la sua variabile. cane1.mettizampe(4); // Chiamiamo il metodo mettizampe() di cane1 cane2.mettizampe(4); // e quello di cane2, per variare le loro millepiedi.mettizampe(1000); // variabili private. Stesso per millepiedi // Gli altri parametri li chiediamo all'utente. cout << "\nLunghezza cane 1? "; // Stampa la frase come un printf() cin >> temp; // Legge il valore come scanf() cane1.mettilungh(temp); // Passa a mettilungh la variabile temp cout << "Lunghezza cane 2? "; // Stampa la frase come un printf() cin >> temp; // Legge il valore come scanf() cane2.mettilungh(temp); // Passa a mettilungh la variabile temp cout << "Lunghezza millepiedi? "; // Stampa la frase come un printf() cin >> temp; // Legge il valore come scanf() millepiedi.mettilungh(temp); // Passa a mettilungh la variabile temp cout << "Num. esatto piedi del millepiedi? "; // Stampa la frase. cin >> temp2; // Legge il valore. millepiedi.mettizampe(temp2); // Passa a mettizampe la variabile temp2 // Ora visualizziamo lo stato degli animali, usando i loro metodi di lettura. cout << "\nStato del cane1: zampe = " << cane1.leggizampe(); cout << ", lunghezza = " << cane1.leggilungh(); cout << "\nStato del cane2: zampe = " << cane2.leggizampe(); cout << ", lunghezza = " << cane2.leggilungh(); cout << "\nStato del millepiedi: zampe = " << millepiedi.leggizampe(); cout << ", lunghezza = " << millepiedi.leggilungh() << "\n"; return 0; // main() restituisce 0 } /* Avrete notato come ogni metodo (funzione) sia preceduto da un campo di * commento in cui si definisce chiaramente il suo scopo, e in particolare * i parametri in entrata ed uscita. Il programma funziona anche senza * commenti, ma con essi diventa comprensibile anche ad altri, e a noi, * nel caso si rispolverasse dopo anni, una volta dimenticato tutto. * Purtroppo molti non commentano assolutamente i listati, e cio' lo credo * un errore grave quanto quelli che causano malfunzionamenti. * In questo esempio abbiamo fatto un passo avanti nella OOP, ma si puo' * benissimo fare una cosa simile in C standard, accedendo alle variabili * di strutture solo tramite funzioni, e non direttamente. Fino ad un * certo punto, infatti, si puo' programmare ad oggetti anche in C * standard, se si seguono le regole della buona programmazione, ossia * dividendo il programma in funzioni, e accedendo ai dati solamente dalle * funzioni ad essi relative, senza mai accederci dal main() o da funzioni * distanti "tipologicamente". */