Dipartimento di fisica “E


Figura 2: Interaction Diagram (in forma semplificata) del programma di simulazione del moto dei corpi celesti in un sistema solare



Scaricare 1.8 Mb.
Pagina5/24
12.12.2017
Dimensione del file1.8 Mb.
1   2   3   4   5   6   7   8   9   ...   24

Figura 2: Interaction Diagram (in forma semplificata) del programma di simulazione del moto dei corpi celesti in un sistema solare

Incapsulamento




Caratteristiche di una Classe


Una Classe e' definita dai suoi Attributi, cioe' da tutto cio' che la caratterizza, e dai Metodi, cioe' dalle operazioni, nel senso piu' ampio possibile, non solo nel senso delle operazioni matematiche, che possono essere effettuate sugli Oggetti della Classe o dagli Oggetti della Classe.

Gli Attributi sono essenzialmente altri Oggetti, tra i quali devono essere ovviamente inclusi gli Oggetti gia' presenti come tipi pre-definiti, cioe' i numeri reali, gli interi, i caratteri, ecc. Ad esempio: gli Attributi di un vettore in tre dimensioni sono tre numeri reali, gli Attributi di un numero complesso sono due numeri reali, ecc. Spesso la scelta degli Attributi non e' univoca (vedi Esercitazione di Laboratorio n. 2) e l'opportunita' di usare l'una o l'altra possibilita' dipende dal problema specifico, in pratica dagli use case. Ad esempio, rappresentare un vettore nello spazio in coordinate cartesiane, in coordinate polari o in coordinate cilindriche e la scelta migliore dipendera' da quale e' l'uso che ne voglio fare.

I Metodi rappresentano le operazioni che gli Oggetti possono effettuare o che posso effettuare su di essi. Ad esempio, dato un vettore in tre dimensioni, potrei essere interessato a calcolare il modulo o le tre componenti cartesiane: ci sara' allora un metodo che mi restituira' il modulo, un altro che mi dara' la componente x o quella y, ecc.

Tutti questi aspetti saranno molto piu' chiari nel prossimo paragrafo, quando costruiremo in C++ la Classe CorpoCeleste. Per ora possiamo limitarci a queste idee generali, che si trovano schematizzate in figura 3, aggiungendo solo due ulteriori osservazioni.




Figura 3: Schematizzazione della struttura di una Classe. Sono anche indicate le funzioni dei metodi di tipo Set e di tipo Get, come discusso nel testo.


La prima osservazione riguarda il tipo degli Attribuiti e dei Metodi: sia gli Attributi che i Metodi possono essere public, private o protected. La differenza tra questi tre casi dipende dal livello di protezione rispetto all'accesso dall'esterno agli Attributi o ai Metodi della Classe, e permette quindi di realizzare l'Incapsulamento. Se un Attributo o un Metodo sono dichiarati private, essi sono accessibili solo da parte di Oggetti della stessa Classe, mentre se sono dichiarati public sono accessibili da tutti. Il caso protected permette di realizzare una situazione intermedia che discuteremo in seguito quando affronteremo l'ereditarieta'. In genere, come regola che puo' funzionare bene per un corso introduttivo, gli Attributi sono private o protected, perche' voglio proteggerli da un accesso esterno, mentre i Metodi sono definiti public, poiche' voglio che tutti possano utilizzare i Metodi che ho definito per ciascuna Classe.

Nella figura 3 sono anche indicati i Metodi di tipo Get e di tipo Set: indichiamo con questi nomi i metodi che vengono utilizzati per ottenere i valori degli Attributi (tipo Get) o per assegnare i valori degli Attributi (tipo Set). I metodi di questo tipo permettono quindi di accedere agli Attributi della Classe, attraverso le possibilita' esplicitamente previste dal programmatore. Per chiarezza e' bene pero' precisare che metre la distinzione tra public, private o protected e' una distinzione fondamentale, interna al linguaggio C++ e alla logica della Classi e codificata dal punto di vista della sintassi del linguaggio, la distinzione tra metodi di tipo Get e di tipo set e' utile per farci capire il ruolo di alcuni metodi, ma non ha la stessa valenza di universalita' e di distinzione fondamentale.




La Classe CorpoCeleste: Attributi e Metodi


In base alle discussioni precedenti, possiamo rappresentare con uno schema la struttura della Classe Corpoceleste:

Nome (stringa)

m (num. reale)

x (num. reale)

y (num. reale)

vx (num. reale)

vy (num. reale)





CorpoCeleste()

~CorpoCeleste()

CalcolaPosizione(forza, dt)

StampaVelocita()

StampaPosizione()

M()


X() Y()

Vx() Vy()



Tra i Metodi indicati ne compaiono due in particolare, il Costruttore, Corpoceleste() ed il distruttore, ~CorpoCeleste(). I loro dettagli, le possibili varianti ed il loro uso saranno chiari nel seguito e verranno studiati nel corso delle prime esercitazioni di laboratorio.

La costruzione esplicita di una Classe passa attraverso due passi, che devono essere tenuti ben distinti, sia dal punto di vista concettuale che dal punto di vista della realizzazione del codice:

i) La dichiarazione della Classe ( .h )

Viene dichiarata la struttura della Classe

La dichiarazione viene scritta in un file di tipo testo,

che si chiama header file, ed ha estensione .h

NomeClasse.h

ii) L'implementazione della Classe ( .cc )

Vengono esplicitamente indicate tutte le operazioni

che caratterizzano ogni metodo

La dichiarazione viene scritta in un file di tipo testo,

che si chiama implementation file, ed ha estensione .cc

NomeClasse.cc




Definizione della classe CorpoCeleste: l'header file CorpoCeleste.h


La definizione della classe CorpoCeleste e' contenuta nel file CorpoCeleste.h, che assume la forma seguente:
class CorpoCeleste {

private:


char *Nome ;

float m ;

double x ;

double y ;

double vx ;

double vy ;

public:

CorpoCeleste();

CorpoCeleste(const char *nomeCorpo, float mass, float xi,

float yi, float vxi, float vyi);

~CorpoCeleste();

void calcolaPosizione(float fx, float fy, float dt);

void stampaPosizione();

const char* nome() ;

float M();

double X();

double Y();

double Vx();

double Vy();

};

La prima riga indica che stiamo definendo una classe di nome CorpoCeleste. La sua definizione inizia con l’apertura della parentesi graffa e termina con la sua chiusura. Tutto ciò che è racchiuso entro queste parentesi appartiene alla classe CorpoCeleste. Ogni istruzione in C++ termina con un segno di punto e virgola ( ; ). Anche la definizione di una classe termina con tale segno.



La parola chiave private (privato) indica, come gia' osservato, che gli attributi e i metodi che seguono sono di esclusiva pertinenza della classe e non possono essere visti all’esterno di essa. Allo stesso modo, la parola chiave public invece indica che gli attributi e i metodi che seguono sono pubblici e possono essere invocati anche dall’esterno della classe.

La riga float m indica che tra gli attributi della classe ve ne è uno che indichiamo con la lettera minuscola m e che rappresenta un numero reale (float). Analogamente velocità e posizione sono rappresentate da coppie di numeri reali che però abbiamo dichiarato come double, cioè come numeri reali in doppia precisione (si tratta di un sistema utilizzato per eseguire dei calcoli più precisi, memorizzando un maggior numero di cifre significative). Il nome di un CorpoCeleste, invece, è memorizzato nella variabile Nome che è una variabile di tipo char*. Il simbolo * che precede il nome di una variabile indica che la variabile in questione rappresenta un puntatore: essa non contiene il valore della variabile, ma l’indirizzo di memoria al quale comincia la memorizzazione della variabile stessa. Per un approfondimento sul ruolo dei puntatori si veda la sezione della parte di sintassi ad essi dedicata: l'importanza dei puntatori diverra' comunque sempre piu' chiara a mano a mano che ci addentreremo in concetti tipici del C++ e della Programmazione ad Oggetti, quali l'Ereditarieta' ed il Polimorfismo e quando studieremo i temi della gestione dinamica della memoria. I puntatori permettono inoltre di fare riferimento ad oggetti, anche molto complessi, semplicemente utilizzando il loro indirizzo. L’uso dei puntatori e' comunque molto semplice: solitamente non occorre curarsi di come l’oggetto viene rappresentato all’interno della memoria e l’accesso ai dati di un puntatore è quasi identico a quello di una variabile statica.

Occorre evidenziare che il nostro esempio non è completamente corretto dal punto di vista della metodologia: per le coordindate e la velocità di un CorpoCeleste avremmo dovuto, infatti, avvalerci di una classe del tipo vettore, ad esempio. Per il nome del corpo invece avremmo potuto utilizzare delle classi standard che rappresentano delle stringhe. Il motivo per cui adoperiamo invece delle semplici variabili consiste nel fatto che, almeno in questa prima fase, è più semplice mantenere un numero limitato di classi nel programma. Inoltre questo modo di procedere ci consente di introdurre concetti nuovi come i puntatori e di imparare l'uso di variabili che sono comunque ancora molto utilizzate. Quanto detto sarà in generale vero per tutti i capitoli di questo corso, pertanto sarà possibile, al termine del corso stesso, trovare poco corrette alcune parti del codice. Sarà un utile esercizio per lo studente quello di modificare gli esempi riportati in modo da rispettare le prescrizioni della programmazione Orientata agli Oggetti.

Per quanto riguarda i metodi osserviamo che essi seguono la riga public: ciò indica che essi sono pubblici, possono cioè essere visti dagli altri oggetti, a differenza dei membri che sono privati. I metodi sono elencati facendo precedere il loro nome dal tipo di variabile che, eventualmente, restituiscono in uscita. Quando infatti un metodo viene invocato, esso può produrre come risultato, oltre ad una transizione nello stato interno di un oggetto, un valore di un particolare tipo (anche complesso). In pratica un metodo si comporta per certi versi come una funzione matematica: si invoca passando alcuni argomenti e se ne ottiene un valore. I metodi che non restituiscono nulla sono di tipo void. L'indicazione del tipo e' obbligatoria, e per questo motivo si utilizza void per i metodi che non restituiscono nulla.

Il metodo calcolaPosizione ad esempio è uno di questi. Esso modifica internamente il valore delle variabili x e y, una volta note le componenti della forza agenti sul corpo e il tempo durante il quale tale forza agisce. Queste grandezze esterne, che non fanno parte delle caratteristiche dell’oggetto, ma che l’oggetto deve prendere dall’esterno, vanno passate come parametri del metodo. Per ogni parametro deve essere specificato il tipo. Poichè una volta calcolata la posizione non si produce alcun risultato (se non il cambiamento dello stato interno delle variabili) il metodo non produce un risultato e pertanto è di tipo void.

Lo stesso vale per il metodo stampaPosizione. Si noti che esso non ha bisogno di parametri per funzionare (non occorre cioè specificare alcuna grandezza esterna all’oggetto). E’ per questo motivo che tra le parentesi non vi sono variabili. Le parentesi d’altra parte indicano che stampaPosizione è un metodo e non un attributo.

Vi sono poi i metodi che, invece, restituiscono dei valori, cioe' i metodi di tipo Get. Sono tali i metodi che consentono di accedere ai valori delle variabili interne (private) della classe senza perdere l’incapsulamento: M(), X(), Y(), Vx() e Vy() sono metodi (poichè vi sono le parentesi) che restituiscono il valore di una variabile che in un caso è un float e negli altri è un double.

Abbiamo poi due tipi particolari di metodi: entrambi hanno lo stesso nome della classe, non hanno tipo e uno di essi è preceduto da una tilde (~). Si tratta dei costruttori e del distruttore della classe.

Nel caso dei costruttori utilizziamo il plurale poiche' ci possono essere piu' tipi di costruttori, che differiscono tra loro per alcuni dettagli di implementazione, come sara' chiaro in seguito, ma concettual-mente si tratta di un metodo caratterizzato dalla funzione di creare un oggetto della classe. Un costruttore infatti è un metodo che può essere invocato una sola volta per oggetto e che realizza la creazione di una istanza della classe, cioe' istanzia (questo e' il termine tecnico per dire "crea") un oggetto della classe e consente l’inizializzazione dei suoi attributi. Ci possono essere piu' costruttori nel senso che non tutti gli attributi possono essere inizalizzati: nel nostro esempio ci sono due costruttori, uno che inizializza tutti gli attributi della classe (quello con i valori dei parametri indicati tra parentesi), ed uno che non ne inizializza alcuno (a volte viene chiamato, un po' gergalmente, "costruttore vuoto", poiche' non ha parametri). Osser-viamo che ancora una volta stiamo realizzando un incapsulamento: stiamo cioè assegnando i valori iniziali ai membri privati della classe passando attraverso un membro pubblico che è il suo costruttore. Questo completa quanto gia' abbiamo detto a riguardo dei metodi di tipo Set: gli attributi di un oggetto possono essere assegnati solo attraverso il Costruttore o i metodi di tipo Set appositamente implementati.

Il distruttore serve invece a rimuovere l’oggetto dalla memoria del computer una volta che non sia più necessario. Come tale non ha parametri.







Condividi con i tuoi amici:
1   2   3   4   5   6   7   8   9   ...   24


©astratto.info 2019
invia messaggio

    Pagina principale