/* 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".
*/
