Sei sulla pagina 1di 30

Object Oriented Programming 1

Object
Oriented
Programming
Copyright 2006-2013 by Andrea Diamantini <adjam7@gmail.om!
"#e$t%opera & $tata rila$iata $otto la lien'a Creati(e Common$ Attrib#'ione-)on ommeriale-Condi(idi allo $te$$o
modo 2.* +talia. ,er leggere #na opia della lien'a (i$ita il $ito -eb http.//reati(eommon$.org/lien$e$/by-n-$a/2.*/it/ o
$pedi$i #na lettera a Creati(e Common$0 171 1eond 1treet0 1#ite 3000 1an 2rani$o0 Cali3ornia0 4510*0 61A
Object Oriented Programming 2
Introduzione alla OOP
Lidea di modificare i tradizionali canoni della programmazione strutturata nasce con lidea che questa
tecnica di programmazione non sia adatta a garantire un sufficiente riutilizzo del codice implementato.
Lidea nuova, come al solito molto semplice, rivoluziona le tecniche precedenti, inserendo la
dimensione rappresentata dai dati del problema.
I dati escono dal concetto precedente di input e output per una scatola nera che li elabora per
assumere la forma di veri e propri oggetti con cui interagire. Ogni singolo dato diventa dunque un
oggetto, da cui il nome di 7bjet 7riented ,rogramming, OOP.
Gli oggetti
Un oggetto un!entit" molto semplice, tipica anche della vita comune, definibile elencando le sue
aratteri$tihe e descrivendo il modo con cui interagisce con lambiente esterno, cio i suoi
omportamenti.
#d esempio, unautomobile un oggetto che ha come caratteristiche $i dati% la marca, il modello, il
colore, la cilindrata... mentre come comportamenti $le funzioni% quelli di avviarsi, spegnersi, fermarsi,
accellerare, curvare e cos& via.
8e aratteri$tihe0 o attributi0 $ono 9#egli elementi #tili a de$ri(ere le propriet: e lo $tato di #n
oggetto. 1ono $olitamente indi(id#ate tramite dei $o$tanti(i.
+ omportamenti0 o metodi $ono 9#elle 3#n'ionalit: he l;oggetto mette a di$po$i'ione per interagire
on e$$o. 1ono $olitamente indi(id#ati tramite dei (erbi.
'li attributi e metodi definiti allinterno di un oggetto vengono comunemente definiti i membri
delloggetto.
Le propriet degli oggetti
(al punto di vista di OOP, un oggetto dunque un contenitore di strutture dati, che rappresentano i suoi
attributi e di funzioni, che rappresentano i suoi metodi. )uesto accorpamento in ununica entit" di tutte
queste informazioni viene detto inap$#lamento.
Object Oriented Programming 3
8;incapsulamento < la aratteri$tia degli oggetti in 77, di inorporare al loro interno attrib#ti e
metodi0 a de3inire #n;#nia entit:.
Incapsulamento un termine tecnico e una procedura rigida che serve a descrivere formalmente una
tendenza tipica del pensiero umano. *e pensiamo alloggetto automobile infatti, viene naturale
collegare ad esso la stringa marca o modello, lintero cilindrata, le funzioni avvia, accellera o curva.
*trettamente legato al concetto di incapsulamento, troviamo quello di interfaccia delloggetto. )uando
si utilizza un automobile, tutti sanno come accellerare, girare, compiere manovre con essa+ di sicuro
per, non tutti conoscono i meccanismi tramite i quali lautomobile in grado di compiere certe
operazioni-
(el resto lautomobile mette a disposizione strumenti semplici $volante, cambio, pedali...% per la
configurazione e lutilizzo. .os& con gli oggetti i programmatori sono interessati a sapere cosa fare per
ottenere un comportamento da un oggetto, non come questo riesca a eseguirlo.
In maniera del tutto generica definiremo dunque interfaccia linsieme di strumenti che ogni oggetto
espone allesterno per il suo utilizzo.
8;interfaccia di #n oggetto < 3ormata dall;in$ieme dei metodi he 9#e$to e$pone al p#bblio0 p#r
na$ondendone la parte implementati(a degli $te$$i.
Una ulteriore propriet" degli oggetti, che permette anche di chiarire meglio le precedenti quella
relativa allinformation hiding.
8;information hiding < la aratteri$tia tipia degli oggetti di na$ondere in3orma'ioni al loro interno
$en'a renderle (i$ibili dall;e$terno.
(a notare il fatto che esporre uninterfaccia per permettere lutilizzo di un oggetto garantisce il rispetto
dellinformation hiding sulle implementazioni dei metodi degli oggetti.
La OOP definisce per ogni membro di un oggetto uno scope ovverosia una visibilit" verso lesterno.
In ogni oggetto troviamo tipicamente/
una sezione pubblica, in cui inserire quegli attributi e metodi che definiranno la sua interfaccia
verso lesterno+
una sezione privata, in cui definire quegli attributi e metodi di suo esclusivo uso e consumo+
Object Oriented Programming 4
Gli oggetti in C++
Il linguaggio .00 utilizza listruzione class per definire gli oggetti, da cui il termine classe come
sinonimo di oggetto.
Una classe una struttura in grado di contenere variabili, che identificheranno gli attributi delloggetto
e funzioni, che definiranno i metodi dello stesso.
Listruzione class realizza dunque la propriet" di incapsulamento, mentre le clausole private e public
descrivono lo scope per ogni membro della stessa.
1ediamo un esempio/
class Automobile
{
private:
int numero_porte;
char colore[50];
char modello[50];
char marca[50];
public:
int velocita;
int marcia;
void avviati();
void fermati();
void accellera();
void gira(int direione);
!;
Limplementazione dei metodi stata volutamente omessa. Per utilizzare la classe bisogner" definirne
una istanza. Una istanza sta ad una classe come una variabile sta al suo tipo. (efiniamo ad esempio/
Automobile afira;
La dimensione di una istanza uguale al numero di b2tes necessari ai suoi attributi, in questo caso
$i'eo3 =int> 3 ? $i'eo3 =har> *0 3 @ 162
3utte i membri della classe #utomobile sono incapsulati al suo interno e sono raggiungibili nelle
istanze tramite loperatore dot $.%
Object Oriented Programming 5
afira"marcia # $;
afira"accellera()+
Potremmo provare a utilizzare anche i membri definiti privati, ma questo violerebbe
la regola dellinformation hiding/
44 566O65 di .O7PIL#8IO95
afira"numero_porte # 5;
#ttributi e metodi definiti private sono utilizzabili solo allinterno di una classe e quindi
nellimplementazione dei suoi stessi metodi.
Linsieme dei membri pubblici di una classe rappresenta la sua interfaccia verso lesterno, cio le
uniche cose che sar" necessario $e possibile% conoscere per utilizzare una classe. Linsieme dei metodi
pubblici di una classe viene solitamente definito API $#pplication Programming Interface% della classe.
Per una leggibilit" del codice migliore, sempre consigliabile definire allinterno della classe solo il
prototipo dei suoi metodi e implementarli quindi separatamente. In questo caso per,, il nome del
metodo va preceduto dal nome della classe con il simbolo // $double:colon, due volte i due punti%
void Automobile::fermati()
{
velocita # 0;
return;
!;
Get/Set functions
9el nostro esempio possiamo ancora notare come gli attributi velocita e marcia siano pubblici,
divenendo modificabili da chiunque, mentre gli altri sono definiti privati, essendo modificabili solo
dallinterno delloggetto. )uesto secondo comportamento solitamente da preferire perch mette al
riparo da eventuali assegnazioni non desiderate
afira"marcia # %; && la afira non ha % marce
afira"velocita # '00; && neanche in discesa""
Object Oriented Programming 6
cos& di solito tutti gli attributi vengono definiti privati e poi si mettono nelle interfacce apposite
funzioni per impostarne $*53% o visualizzarne $'53% il valore. )ueste funzioni sono definite funzioni
'et4*et. 1ediamo una possibile modifica alla nostra classe/
class Automobile
{
private:
int numero_porte;
char colore[50];
char modello[50];
char marca[50];
int velocita;
int marcia;
public:
void avviati();
void fermati();
void accellera();
void gira(int direione);
&& ()* & +)* functions
int velocita();
void set,elocita(int v);
int marcia();
void set-arcia(int m);
!;
Le funzioni '53 hanno solitamente lo stesso nome della variabile a cui si riferiscono, mentre le
funzioni *53 utilizzano il prefisso set: con le solite regole di stile del codice per quanto riguarda i nomi
di funzione.
Object Oriented Programming 7
Esercizi
;. (escrivere una radiosveglia in quanto oggetto, indicando le sue caratteristiche e i suoi
comportamenti e provando a definirne la dichiarazione.
<. (escrivere, tramite uno schema, la classe televisione e la classe telecomando, mettendo in
evidenza quali messaggi possono essere inviati dalla seconda alla prima.
=. (efinire loggetto motore scegliendo accuratamente le caratteristiche da descrivere e mettendo
in evidenza le necessarie funzioni get e set.
>. .reare un oggetto automobile con le opportune caratteristiche, per risolvere il seguente
problema/ qual la velocit" massima a cui un auto pu, andare per avere uno spazio di frenata
inferiore ai ;?? metri@
Object Oriented Programming 8
Metodi predefiniti di un oggetto
Ogni volta che viene creata una nuova classe vengono definiti quattro metodi di default/
;. .ostruttore
<. .ostruttore di copia
=. (istruttore
>. loperatore di assegnazione fra istanze
)uesti quattro metodi sono sempre presenti in ogni classe, anche se non vengono esplicitamente
definiti. La possibilit" del programmatore quella di ridefinirli e personalizzarli secondo necessit".
Costruttore
In una classe possibile definire un metodo senza output che abbia lo stesso nome della classe. )uesta
funzione prende il nome di costruttore e viene richiamata automaticamente alla creazione di unistanza.
)uando unistanza definita come variabile globale $fuori da una funzione%, il costruttore viene
richiamato allavvio del programma, mentre se listanza definita localmente, il costruttore viene
richiamato nel momento in cui questa viene resa disponibile.
I costruttori sono funzioni fondamentali nella OOP perch permettono di preparare loggetto
allutilizzo in modo chiaro e semplice. )uando non vengono definiti esplicitamente, i costruttori di
default assegnano a valori predefiniti tutti gli attributi della classe $es. gli interi a zero, le stringhe alla
stringa nulla, etc..%.
1ediamo un esempio di dichiarazione esplicita/
class Automobile
{
private:
int numeroPorte;
int cilindrata;
public:
Object Oriented Programming
Automobile()
{
numeroPorte = 5;
cilindrata = 1600;
};
};
)uando poi si dichiara unistanza per la classe, il costruttore viene lanciato automaticamente e quindi
loggetto risulta inizializzato/
Automobile zaira;
cout !! "numero porte: " !! zaira#numeroPorte !! endl ;
cout !! "cilindrata: " !! zaira#cilindrata !! endl ;
Il costruttore va indicato praticamente in tutte le occasioni fra i membri pubblici di una classe, in modo
che sia disponibile quando qualcuno vuole utilizzare unistanza della classe in questione.
.ome ogni altra funzione, il costruttore pu, prevedere luso di parametri per personalizzare
linizializzazione/
Automobile(int p$ int c)
{
numeroPorte = p;
cilindrata = c;
};
in questo modo potremo definire unistanza della classe semplicemente dichiarando
Automobile zaira(5$ 1600);
Automobile %10(&$ 1&00);
5ntrambi i costruttori possono essere definiti allinterno della classe in modo che lutente non debba far
altro che scegliere quale usare/
Automobile %10(&$ 1&00);
Automobile utilitaria;
'' (uanto vale utilitaria#cilindrata)
Object Oriented Programming 1!
Costruttore di copia
Un particolare costruttore con parametri, quello che ha come unico parametro un elemento stesso della
classe, il costruttore di copia. )uesta funzione di default crea una copia esatta dellistanza passata
come parametro/
Automobile zaira(6$ 1500);
Automobile m%*ar(zaira);
'' m%*ar +a ,li stessi valori nei parametri di zaira
5 comunque possibile ridefinire a piacere il proprio costruttore di copia, utilizzandolo per gli usi piA
disparati/
'' con (uesto cop% constructor - possibile instanziare
'' al massimo una & porte di cilindrata 1.00
Automobile(Automobile p)
{
numeroPorte = &;
i (p#cilindrata!1.00)
cilindrata = 1.00;
else
cilindrata = p#cilindrata;
};
(i certo per, il costruttore di copia piA utilizzato quello predefinito. )uesta funzione viene infatti
solitamente utilizzata per replicare velocemente le istanze.
Distruttore
Un altro metodo particolare, definito senza output e che ha lo stesso nome della classe perceduto da un
B $tilde% viene comunemente definito distruttore. )uesto significa che il distruttore della classe
C#utomobile la funzione CB#utomobile$%.
Il distruttore viene eseguito ogni qualvolta un oggetto perde visibilit", a causa del termine del
programma o di una gestione automatica della memoria.
Object Oriented Programming 11
/Automobile()
{
cout !! 0b%e b%e1 !! endl;
};
5ssendo il distruttore lultima funzione che verr" eseguita prima di liberare la memoria, opportuno
considerare di inserire ogni sorta di operazione finale in essa. *alvare i dati importanti su file,
cancellare le locazioni di memoria dinamiche, confermare la chiusura di un programma.
Operatore di assegnazione fra istanze
Lassegnazione fra istanze di una stessa classe causa la copia dei valori di tutti gli attributi dalla
seconda alla prima istanza. *ostanzialmente questo comporta che fra due istanze della stessa classe sar"
possibile utilizzare loperatore D
Automobile %10(&$1.00);
Automobile utilitaria:
utilitaria = %10;
9on dunque necessaria nessuna sorta di definizione per un operatore specifico di assegnazione, in
quanto il compilatore ne crea automaticamente uno di default, rendendo le cose molto naturali, come
fra i tipi semplice di variabile.
*e infatti abbastanza chiaro cosa significa che due numeri interi sono uguali, altrettanto non si pu,
dire parlando degli oggetti piA disparati.
Object Oriented Programming 12
Esercizi
;. (efinire una classe Persona con un costruttore che assegna nome e cognome e uno predefinito
con nome D nome e cognome D cognome.
<. .reare un oggetto 6ettangolo, fornito di un costruttore di copia che crea un nuovo rettangolo di
#rea doppia del precedente.
=. .reare una classe per descrivere il comportamento di un conto corrente bancario, fornendo la
possibilit" di eseguire prelievi, depositi e visualizzazione saldo corrente. La classe avr" inoltre
un distruttore che salva il saldo corrente su file e un costruttore che carica i dati se presenti.
>. .reare un oggetto utente che contenga almeno i campi username, mail, passEord e fornire i
metodi appropriati per inserimento,$occhio alla sicurezza della passEord--%.
F. .reare una lista per la gestione del prestito libri in una biblioteca. Le informazioni da registrare
riguardano/ il numero progressivo del prestito, il codice della tessera, il codice del libro, la data
di scadenza del prestito. #l termine della giornata si deve poter visualizzare lelenco dei prestiti
in corso $elenco della lista%. *i implementi il metodo di caricamento della lista e il metodo di
visualizzazione del contenuto.
Ipotizzare che la biblioteca disponga di <??? libri.
G. (urante le elezioni politiche, una societ" specializzata nei sondaggi raccoglie i dati alluscita di
un seggio elettorale. In qualunque momento la societ" deve essere in grado di fornire una
statistica delle opinioni raccolte/ numero dei votanti, numero degli intervistati, totale preferenze
espresse dagli intervistati e relative percentuali. *i supponga che al seggio siano iscritti F???
elettori attivi.
Object Oriented Programming 13
"reditariet#
In informatica l!ereditariet" H uno dei concetti fondamentali nel paradigma di programmazione a
oggetti. 5ssa consiste in una relazione che il linguaggio di programmazione, o il programmatore stesso,
stabilisce tra due classi. # seconda del linguaggio di programmazione, l!ereditariet" pu, essere
ereditariet" singola o semplice $ogni classe pu, avere al piA una superclasse diretta% o multipla $ogni
classe pu, avere piA superclassi dirette%.
In generale, l!uso dell!ereditariet" d" luogo a una gerarchia di classi+ nei linguaggi con ereditariet"
singola, si ha un albero se esiste una superclasse IradiceI unica di cui tutte le altre sono direttamente o
indirettamente sottoclassi $come la classe ObJect nel caso di Kava% o a una foresta altrimenti+
l!ereditariet" multipla definisce invece una gerarchia a grafo aciclico diretto.
Ereditariet delle classi
L ereditariet" uno dei concetti fondamentali nel paradigma di programmazione a oggetti. 5ssa
consente di definire una classe come classe derivata a partire da una classe preesistente detta classe
base. La sottoclasse eredita implicitamente tutte le caratteristiche $attributi e metodi% della classe base.
9el linguaggio .00, la classe derivata viene definita a partire da una o piA classi di base con la
seguente sintassi/
class *lasse2erivata : !scope3 *lasse4ase
In questo modo si ottiene una classe che a seconda della visibilit" $scope% ereditata, ha gi" definiti tutta
una serie di attributi e metodi derivati. # livello di visibilit", oltre alle clausole public e private, si
aggiunge anche la clausola protected che descrive tutti quegli attributi, metodi, classi che sono visibili
solo alla classe in cui sono definiti $come i private% e nelle classi derivate. La visibilit" della classe base
determina quella dei membri derivati, secondo la seguente tabella/
Membri
classe base
Diventano con spec.
Public
Diventano con spec.
protected
Diventano con spec.
private
Public public protected Private
protected Protected protected Private
private 9on accessibili 9on accessibili 9on accessibili
Object Oriented Programming 14
1ediamo dunque un esempio basato su una generica classe LiguraPiana qui definita/
class 5i,uraPiana
{
protected:
loat lar,+ezza;
public:
loat area()
{
return 0;
};
loat perimetro()
{
return 0;
};
};
class 6ettan,olo : public 5i,uraPiana
{
protected:
loat base;
loat altezza;
public:
6ettan,olo(loat$ loat);
loat area();
loat perimetro();
};
I metodi area e perimetro vengono ereditati dalla classe base, mentre lattributo larghezza no, perch
stato dichiarato privato. Per averlo accessibile nella classe derivata, ma non dallesterno $come sempre
buona norma per gli attributi% sarebbe stato necessario definirlo protected. Le funzioni area e perimetro
possono essere direttamente reimplementate/
loat 6ettan,olo::area()
{
return base 7 altezza;
}
loat 6ettan,olo::perimetro()
{
return ( base 8 altezza ) 7 .;
}
#llo stesso modo si pu, procedere con una nuova redefinizione della classe 6ettangolo/
Object Oriented Programming 15
class 9rian,olo6ettan,olo : public 6ettan,olo
{
private:
9rian,olo6ettan,olo();
double ipotenusa();
loat area();
};
loat perimetro();
loat 9rian,olo::area()
{
return (base 7 altezza)'.;
}
loat 9rian,olo::perimetro()
{
return base 8 altezza 8 ipotenusa();
}
double 9rian,olo::ipotenusa()
{
return s(rt(base7base 8 altezza7altezza);
}
In questo secondo caso abbiamo ridefinito limplementazione di metodi ereditati $overriding% e
aggiunto un metodo supplementare. In generale ogni classe derivata pu,/
;. ereditare attributi e metodi dalla classe base $secondo le regole definite%+
<. estendere la classe base definendo nuovi membri nella classe derivata+
=. ridefinire attributi e funzioni membro $overriding%
Costruttori, distruttori ed ereditariet
5ssendo costruttori e distruttori dei metodi predefiniti in ogni classe, ovvio aspettarsi che saranno
automaticamente disponibili sia nelle classi base che in quelle derivate, come il costruttore di copia e
loperatore di assegnazione fra istanze. #l contrario degli altri due per,, costruttore e distruttore hanno
una particolarit" in piA, perch sono metodi eseguiti automaticamente alla definizione di una istanza $il
costruttore% e alla sua eliminazione $il distruttore%. 9el meccanismo dellereditariet" si incastrano
dunque costruttori e distruttori delle classi base e delle classi derivate.
)uando si crea un oggetto di una classe derivata, il costruttore della classe base viene lanciato
automaticamente e prima di quello della classe derivata. #nalogamente quando si distrugge un oggetto
di una classe derivata viene lanciato prima il distruttore della classe derivata e poi il distruttore della
Object Oriented Programming 16
classe base.
)ueste operazioni sono completamente automatiche e non c possibilit" di agire in maniera differente.
Infatti se si hanno una classe base # e una classe derivata M e si crea un oggetto N della classe M, tutte
le variabili membro di M che derivano da # devono essere in qualche modo inizializzate e il metodo
istituzionale per farlo il costruttore della classe da cui derivano. #llo stesso modo, gli attributi
presenti in M ma non in # saranno inizializzati dal costruttore di M.
La chiamata di costruttori e distruttori in fase di creazione e distruzione delle istanze sar" iterata poi per
tutta la genealogia della classe di appartenenza. 1ediamo un esempio per chiarire meglio il concetto.
class A
{
public:
A();
/A();
};
A::A()
{
cout !! "costruttore A" !! endl;
}
A::/A()
{
cout !! "distruttore A" !! endl;
}
'' :::::::::::::::::::::::::::::::::::::::
class 4 : public A
{
public:
4();
/4();
};
4::4()
{
cout !! "costruttore 4" !! endl;
}
4::/4()
{
cout !! "distruttore 4" !! endl;
}
'' :::::::::::::::::::::::::::::::::::::::
class * : public 4
{
public:
*();
/*();
Object Oriented Programming 17
};
*::*()
{
cout !! "costruttore *" !! endl;
}
*::/*()
{
cout !! "distruttore *" !! endl;
}
int main()
{
* ob;;
return 0;
}
Il precedente codice restituisce come output/
costruttore A
costruttore 4
costruttore *
distruttore *
distruttore 4
distruttore A
cio la semplice dichiarazione delloggetto obJ della classe . provoca lesecuzione del costruttore di #,
poi di M, poi di .. La distruzione delloggetto, che in questo avviene alla fine del programma, provoca
lesecuzione del distruttore di ., poi di M infine di #.
9el caso di un passaggio necessario di parametri fra il costruttore della classe derivata e la sua classe
base, bisogner" procedere nel seguente modo. (efiniamo una classe Linea in questo modo/
class <inea
{
protected:
loat lun,+ezza;
public:
<inea(loat l);
};
<inea::<inea(loat l)
{
lun,+ezza = l;
}
*e vogliamo derivare una classe 6ettangolo da questa, baster" aggiungere ai suoi attributi un nuovo
float per laltezza/
Object Oriented Programming 18
class 6ettan,olo : public <inea
{
private:
loat altezza;
public:
6ettan,olo(loat l$ loat +);
};
# questo punto, per limplementazione del costruttore della classe 6ettangolo dobbiamo passare il
primo parametro al costruttore della classe base Linea, che definisca la lunghezza.
)uesta operazione viene effettuata tramite loperatore / lanciato dopo la segnatura del costruttore da
implementare e seguito dal costruttore della classe base da lanciare/
6ettan,olo::6ettan,olo(loat l$ loat +) : <inea(l)
{
altezza = +;
}
Object Oriented Programming 1
Esercizi
;. (efinire una o piA gerarchie con i seguenti oggetti e definire le relative classi base e derivate/
Professore, *tudente, Persona, #utista, .amion, #utobus.
<. (efinire la classe )uadrato e poi utilizzarla per derivare le classi .ubo e Piramide. Infine
fornire un test per ognuna delle classi implementate.
=. (efinire la classe base .erchio e poi implementare la classe derivata .ilindro.
>. (efinire la classe #ppuntamento e utilizzarla per definire la classe #genda.
F. (ata la classe
class Mase
O
protected/
int i+
char c+
float f+
double d+
P+
implementare la classe (erivata / public Mase e aggiungere i metodi *53 per gli attributi
derivati.
Object Oriented Programming 2!
Polimorfi$mo
Il termine polimorfismo indica genericamente la capacit" di assumere forme diverse. .os& nella OOP, il
polimorfismo indica la possibilit", per i metodi e le funzioni in genere di assumere forme diverse. Il
concetto strettamente legato allereditariet" tipica delle classi e si pu, manifestare in piA forme/
overriding
il termine indica la possibilit" di reimplentare le funzioni quando queste sono ridefinite in una
classe derivata. )uesta particolare caratteristica conferisce piena funzionalit" al concetto di
ereditariet". 5 la caratteristica che data la classe base 6ettangolo e la classe derivata
3riangolo6ettangolo permette di reimplementare correttamente i metodi area$% e perimetro$%
overloading
il termine indica la possibilit" di definire metodi $o funzioni% omonimi, ma aventi parametri
diversi per numero e tipo. )uesta forma, spesso considerata come il polimorfismo puro
conferisce una grandissima capacit" espressiva alla programmazione funzionale e orientata agli
oggetti.
1ediamo adesso alcuni esempi per comprendere al meglio i concetti illustrati.
Overriding
Loverriding si presenta solo in collegamento allereditariet" e permette la riscrittura del codice nelle
classi derivate di funzioni gi" implementate nelle classi base. Lo scopo di una riscrittura del metodo
ovviamente quello di modificarne il comportamento per adattarlo alle nuove necessit". #d esempio,
data la classe Punto<(, cos& definita/
class Punto.2
{
protected:
loat =;
loat %;
public:
Punto.2(loat$loat);
double distanza();
};
Object Oriented Programming 21
Punto.2::Punto.2(loat A$ loat 4)
{
= = A;
% = 4;
}
double Punto.2::distanza()
{
return s(rt( (= 7 = 8 % 7 %);
}
possiamo semplicemente derivare la classe Punto=(. # questo punto per,, il metodo distanza$% avr"
bisogno di essere reimplementato per funzionare correttamente nella nuova classe.
class Punto&2 : public Punto.2
{
private:
loat z;
public:
Punto&2(loat$ loat$ loat);
double distanza();
};
Punto&2::Punto&2(loat A$ loat 4$ loat *)
: Punto.2(A$4)
$ z(*)
{
}
double Punto&2::distanza()
{
double r1 = s(rt(= 7 = 8 % 7 %);
double r. = s(rt (r1 7 r1 8 z 7 z);
return r.;
}
)uesta necessit" viene soddisfatta completamente dalla caratteristica di overriding. 9el caso particolare
in cui si abbia bisogno di richiamare il metodo della classe base, bisogner" esplicitamente indicarlo
tramite loperatore //
Il metodo Punto=(//distanza$% pu, essere allora cos& reimplementato/
double Punto&2::distanza()
{
double r1 = Punto.2::distanza();
double r. = s(rt ( r1 7 r1 8 z 7 z );
return r.;
}
Object Oriented Programming 22
Overloading
Loverloading, come detto, indica la capacit" di metodi e funzioni di definire metodi omonimi che si
differenziano solo per numero e tipo di parametri. La caratteristica tipica della programmazione ad
oggetti, ma funziona anche nella normale programmazione funzionale.
Un semplice esempio di questo pu, essere la definizione di una semplice funzione/
void >crivi(int i)
{
print("?d"$ i);
}
void >crivi(c+ar c)
{
print("?c"$ c);
}
void >crivi(loat )
{
print("?"$ );
}
*ostanzialmente, definendo una funzione *crivi per ogni tipo di variabile, il programmatore che la
utilizza potr" lanciare la funzione *crivi$p% dove p una variabile di uno qualsiasi dei tipi supportati
visualizzandone il contenuto. PiA o meno allo stesso modo definita la funzione std//cout tipica del
linguaggio .00.
#pplicare questa caratteristica ai metodi delle classi significa poter definire una serie di metodi per
eseguire un compito e utilizzarle a seconda degli input che si possiedono.
(efiniamo una nuova classe Punto<(/
class Punto.2
{
private:
loat =;
loat %;
public:
Punto.2(loat$loat);
double distanza();
double distanza(Punto.2 P);
double distanza(loat =P$ loat %P);
};
nella classe sopra definita abbiamo a disposizione tre metodi distanza$%/ il primo, senza parametri,
Object Oriented Programming 23
calcola la distanza del punto dallorigine+ gli altri due calcolano entrambi la distanza fra il punto
definito e un altro. Loverloading permette di implementare piA funzioni a seconda che del secondo
punto si abbia gi" una dichiarazione oppure se ne conoscano solo le coordinate.
double Punto.2::distanza()
{
return s(rt (= 7 = 8 % 7 %);
}
double Punto.2::distanza(Punto.2 P)
{
return s(rt( (= : P#=)7(= : P#=) 8 (% : P#%)7(% : P#%) );
};
double Punto.2::distanza(loat =P$ loat %P)
{
return s(rt( (= : =P)7(= : =P) 8 (% : %P)7(% : %P) );
};
Esercizi
;. (ate le classi implementate studiando lereditariet"/ Professore, *tudente, Persona, #utista,
.amion, #utobus+ fornire il maggior numero di esempi di overloading e di overriding.
<. (ata una classe con attributi un intero, un reale e un carattere, fornire un polimorfismo della
funzione Inserisci che senza parametri permette di inserire tutti i dati e con i parametri
appropriati permetta linserimento del tipo di dato individuato.
Object Oriented Programming 24
%aratteri$tic&e a'anzate
#lcune caratteristiche della OOP in .00, pur essendo un po! ostiche da comprendere appieno sono
molto utilizzate in programmazione e necessitano dunque la necessaria introduzione. 5sempi ed
applicazioni saranno tralasciati e rimandati a tutti i momenti durante le esercitazioni in cui si verificano
le condizioni per approfittare di una di queste caratteristiche.
Parametri di default
.ome abbiamo visto in precedenza, il polimorfismo, che una delle caratteristiche piA importanti nel
.00 e nei linguaggi orientati agli oggetti, fornisce al programmatore la possibilit" di definire piA
funzioni con lo stesso nome, ma diversa segnatura. )uesta caratteristica viene sfruttata quasi sempre
dai costruttori e in generale da tutte le funzioni che vogliono ad esempio fornire una implementazione
per valori fissi e definiti dal programmatore e una seconda con valori variabili scelti dallutente
$della classe%. Per evitare una inutile proliferazione di funzioni di questo tipo e lasciare lereditariet"
pura per scopi piA alti, si pensato di permettere alle funzioni di definire per i parametri alcuni valori di
default. In sostanza ogni funzione in .00 pu, essere definita specificando un opportuno valore da
passare ad ognuno dei suoi parametri, oppure nessuno, in modo tale che se lutente specifica un valore
per il parametro, questo viene utilizzato, altrimenti si utilizza il valore di default.
1ediamo un esempio/
void (c+ar c = @d@$ int b = .)
{
cout !! c !! " $ " !! b !! endl;
}
(); '' scrive 0d $ .1
(@z@$); '' scrive 0z $ .1
($A); '' scrive 0d $ A1
(@B@$1); '' scrive 0B $ 11
Object Oriented Programming 25
unzioni virtuali
Le funzioni virtuali sono una caratteristica molto comoda per guidare lereditariet". 1engono infatti
solitamente definite nelle classi astratte o comunque nelle classi da non usare direttamente, ma da
derivare per specificarne al meglio limplementazione di alcuni metodi. Una funzione virtuale si
definisce anteponendo la clausola virtual alla segnatura del metodo. #d esempio/
virtual double area();
Lobiettivo delle funzioni virtuali quello di predisporre alla derivazione le classi favorendo
loverriding dei metodi definiti virtuali, ma bloccando loverloading. .io una funzione virtuale pu,
essere ridefinita nella classe derivata solo con la stessa segnatura della classe base.
Un caso interessante di applicazione delle funzioni virtuali relativo al binding dei metodi fra classi
base e derivate. (efiniamo, ancora una volta, la seguente classe/
class 6ettan,olo
{
protected:
loat base$ altezza;
public:
6ettan,olo(loat$loat);
loat area();
loat perimetro();
};
6ettan,olo::6ettan,olo(loat b$ loat +)
{
base = b;
altezza = +;
}
loat 6ettan,olo::area()
{
return base 7 altezza ;
}
loat 6ettan,olo::perimetro()
{
return . 7 (base 8 altezza);
}
aggiungiamo al nostro main una procedura di visualizzazione di area e perimetro di un rettangolo dato/
Object Oriented Programming 26
void Cisualizza2ati(6ettan,olo r)
{
cout !! "Area: " !! r#area() !! endl;
cout !! "Perimetro: " !! r#perimetro() !! endl;
}
Infine deriviamo la classe 3riangolo6ettangolo dalla classe precedente/
class 9rian,olo6ettan,olo : public 6ettan,olo
{
public:
9rian,olo6ettan,olo(loat b$ loat +);
loat ipotenusa();
loat area();
loat perimetro();
};
9rian,olo6ettan,olo::9rian,olo6ettan,olo(loat b$ loat +)
: 6ettan,olo(b$+)
{}
loat 9rian,olo6ettan,olo::ipotenusa()
{
return (loat) s(rt( base 7 base 8 altezza 7 altezza );
}
loat 9rian,olo6ettan,olo::area()
{
return base 7 altezza ' .;
}
loat 9rian,olo6ettan,olo::perimetro()
{
return base 8 altezza 8 ipotenusa();
}
Lin qui risulta tutto nella norma, con lunica eccezione della funzione di visualizzazione, che con un
semplice passaggio di parametri, ci risparmia un p, di fatica nel main. 1ediamolo/
int main()
{
6ettan,olo r(5#0 $ .#5);
cout !! "6ettan,olo" !! endl;
Object Oriented Programming 27
Cisualizza2ati(r);
cout !! "9rian,olo 6ettan,olo" !! endl;
9rian,olo6ettan,olo t(A#0 $ &#0);
Cisualizza2ati(t);
}
Lutilizzo della funzione 1isualizza(ati$% con la classe 3riangolo6ettangolo possibile $e solitamente
risparmia un sacco di fatica, evitando inutili reimplementazioni%, ma in questo caso fallisce. 5cco
loutput generato/
6ettan,olo
Area: 1.#5
Perimetro: 15#0
9rian,olo 6ettan,olo
Area: 1.#0 '' D66E6D: l@area sarebbe u,uale a 6#0
Perimetro: 1A#0 '' D66E6D il perimetro sarebbe u,uale a 1.#0
Lesecuzione in questo caso fallisce perch la funzione *tampa(ati, che ha come parametro un oggetto
della classe 6ettangolo, esegue le funzioni della suddetta classe invece di quelle della classe derivata.
Per ovviare a questo pericolo e predisporre sempre allereditariet" e al giusto overriding i metodi,
necessario dichiararli come virtual/
class 6ettan,olo
{
protected:
loat base$ altezza;
public:
6ettan,olo(loat$loat);
virtual float area();
virtual float perimetro();
};
!inding dinamico
Il polimorfismo $in questo caso loverriding% particolarmente utile quando la versione del metodo da
eseguire viene scelta sulla base del tipo di oggetto effettivamente contenuto in una variabile a runtime
$invece che al momento della compilazione%. )uesta funzionalit" viene definita binding dinamico $o
late:binding%. *e ho una variabile di tipo #, e il tipo # ha due sottotipi $sottoclassi% M e ., che
ridefiniscono entrambe il metodo m$%, loggetto contenuto nella variabile potr" essere di tipo #, M o .,
e quando sulla variabile viene invocato il metodo m$% viene eseguita la versione appropriata per il tipo
Object Oriented Programming 28
di oggetto contenuto nella variabile in quel momento.
Per ritornare allesempio di poco fa, supponiamo che un aereo debba affrontare procedure per larrivo e
la partenza molto piA complesse di un normale camion, come in effetti / allora le procedure arrivo$% e
partenza$% devono essere cambiate rispetto a quelle della classe base 7ezzo(i3rasporto. )uindi
provvediamo a ridefinirle nella classe aereo in modo che facciano quello che necessario
$polimorfismo%/ a questo punto, dalla nostra lista di mezzi possiamo prendere qualsiasi mezzo e
chiamare arrivo$% o partenza$% senza doverci piA preoccupare di che cos loggetto che stiamo
maneggiando/ che sia un mezzo normale o un aereo, si comporter" rispondendo alla stessa chiamata
sempre nel modo giusto.
Il linguaggio .00 per default non usa il binding dinamico e se lo si vuole utilizzare bisogna inserire la
Qe2Eord virtual nella segnatura del metodo interessato.
funzioni virtuali pure
Una funzione virtuale si definisce pura quando non viene implementata nella classe in cui viene
definita. )uesto implica lobbligo per chi utilizza la classe che contiene una funzione virtuale pura, di
derivare la classe e implementare a piacimento la funzione nella classe derivata prima di poterla
istanziare.
9el linguaggio .00 le funzioni virtuali pure vengono dichiarate con una speciale sintassi, come segue/
virtual void do>omet+in,() = 0;
Porre una funzione virtuale uguale a 856O significa renderla virtuale pura. )ueste funzioni servono
per definire le cosiddette classi astratte.
Classi astratte
Le classi vengono solitamente definite elencando $e implementando% attributi e metodi. )uando queste
sono state poi completamente definite, possibile procedere alla loro istanziazione, cio possibile
definire una istanza della classe ed utilizzarla in un qualsiasi programma. 5sistono per, classi che non
possono essere istanziate/ queste classi vengono definite al solo scopo di creare una struttura comune a
diverse situazioni, ma inutilizzabili praticamente in ognuna di queste. )ueste classi si dicono classi
astratte e si distinguono da quelle normali perch almeno uno dei loro metodi non stato implementato.
PiA precisamente, in .00 una classe si dice astratta se contiene almeno una funzione virtuale pura. In
questo modo, nelle classi astratte il focus si sposta dallimplementazione funzionale dei metodi alla
scelta di attributi e metodi generici.
1ediamo un esempio/
Object Oriented Programming 2
class 5i,uraPiana
{
protected:
loat lar,+ezza;
public:
loat area() = 0;
loat perimetro() = 0;
};
5 evidente infatti, che ogni figura piana $es/ rettangolo, triangolo, cerchio% avr" come caratteristiche
una larghezza, unarea e un perimetro. 5 altrettanto ovvio per,, che ognuna di queste figure ha una
formula diversa per il calcolo di queste e non avrebbe quindi senso implementare, in linea generale, un
qualcosa relativo solo ad una particolare figura.
Le classi astratte sono dunque utilizzate per indicare tutte le caratteristiche e funzionalit" comuni ad un
insieme di oggetti. *ar" compito poi di altre classi speciali e derivate da queste implementare i metodi
secondo necessit".
unzioni static"e
Le funzioni statiche di una classe sono quelle funzioni membro precedute dalla clausola static. La loro
caratteristica principale che si possono richiamare indipendentemente dallesistenza di una istanza
della classe. Lo si pu, fare semplicemente ricorrendo al nome della classe 0 operatore di scope $//, i
quattro punti%. *e ad esempio, la classe LiguraPiana avesse una funzione statica contaLigure$%, che
restituisce il numero di istanze di LiguraPiana dichiarate, allora il codice sarebbe piA o meno questo/
class 5i,uraPiana
{
protected:
loat lar,+ezza;
public:
virtual loat area() = 0;
virtual loat perimetro() = 0;
static int contaFigure();
};
###
int n = 5i,uraPiana::conta5i,ure();
Object Oriented Programming 3!
unzioni #e classi$ amic"e
Le funzioni che non appartengono ad una classe non possono accedere ai membri privati $o protetti% di
questa. # volte capita per, di definire due classi che non sono affatto collegate fra loro dal meccanismo
dellereditariet", ma che hanno bisogno di condividere fra loro i valori dei loro attributi privati. *i
presenta qui un caso molto spinoso/ se non c ereditariet" e non si pu, dunque sfruttare il meccanismo
dei membri protetti, visibili alle sottoclassi, la visibilit" si ottiene solo dichiarando un membro public o
tramite funzioni get4set.
In entrambi i casi per, si ha leffetto collaterale di concedere a tutti e non solo alle due classi citate la
visibilit" su tale membro.
In soccorso a queste situazioni, non cos& inusuali, arriva il concetto di funzione o classe amica. Ogni
classe pu, concedere la visibilit" sui suoi membri privati ad una sola classe o funzione dichiarandola
amica $friend%. *e la classe LiguraPiana vuole concedere visibilit" alla classe Liguraccia $che
probabilmente si riferisce a tuttaltro tipo di figure e quindi non c ereditariet" possibile fra loro..%
dovr" dichiarare/
class 5i,uraPiana
{
protected:
loat lar,+ezza;
public:
###
friend class Figuraccia;
};
In questo modo la classe Liguraccia potr" accedere nel suo codice ai membri privati e protetti definiti
nella classe LiguraPiana.