Elementi di
programmazione
ad oggetti
a. a. 2009/2010
Corso di Laurea Magistrale in Ingegneria Elettronica
Docente: Mauro Mazzieri, Dipartimento di Ingegneria Informatica,
Gestionale e dell’Automazione
Lezione 6
Ereditarietà.
Funzioni virtuali e polimorfismo.
Ereditarietà

Processo mediante il quale un oggetto
acquisisce le proprietà di un altro oggetto


Concetto di classificazione gerarchica
Una classe eredita attributi e metodi da
una classe madre



La classe base è detta superclasse
La classe derivata è detta sottoclasse
La sottoclasse può aggiungere, occultare o
ridefinire lo stato e i metodi della superclasse


Grazie all’ereditarietà, la sottoclasse deve definire
solo quelle caratteristiche la rendono unica
Un oggetto della sottoclasse rappresenta un esempio
più specifico di un oggetto della classe più generale
Esempio: classe base

Definisco una classe che rappresenta
un impiegato:
class Impiegato {
string nome;
int salario;
}

Successivamente, voglio definire la
classe dei manager
Esempio: classe derivata
class Manager {
// dati dell’impiegato
string nome;
int salario;
// nuovi dati
int livello;
}
class Manager {
// contiene i dati dell’impiegato
Impiegato i;
int livello;
}
class Manager : public Impiegato {
int livello;
};
Ereditarietà: diagramma UML


Relazione “is-a”
Freccia verso la
classe base

È la classe derivata
che fa riferimento
alla classe base,
non viceversa
Principio di sostituzione

La classe derivata è un sottotipo della
classe base

Posso usare un oggetto della classe
derivata al posto di un oggetto della
classe base
 Non

vale il contrario
Principio di sostituzione di Liskov:
Un oggetto di una classe derivata può
essere ovunque si usi un oggetto della
classe di base
Classi derivate: membri
Un membro di una classe derivata
può utilizzare membri pubblici della
classe base
 I membri pubblici definiti per la classe
base sono implicitamente definiti
(ereditati) dalla classe base


La classe derivata può definire nuovi
membri
Classi derivate: membri privati
e protetti

Una classe derivata non può usare i
membri privati della classe base


… sarebbe possibile accedere alla parte
privata definendo una sottoclasse
Oltre alla parte public e private, nella
dichiarazione di una classe può essere
presente una parte protected

I membri protetti sono come membri
privati, salvo che per le classi derivate
Classi derivate: costruttori

I costruttori non si ereditano
Vanno ridefiniti nelle classi derivate
 Un oggetto base deve esistere prima che
possa diventare un oggetto derivato…

…

dunque il costruttore della classe base
deve essere chiamato prima che il
costruttore della classe derivata
Manager::Manager(const string& n,
int s, int l) : Impiegato(n, s),
livello(l) { }
 Lista
di inizializzazione
Classi derivate: distruttori

L’invocazione di un distruttore di una
classe derivata produce
automaticamente una chiamata a tutti
i distruttori delle classi base
I distruttori vengono richamati risalendo
nella gerarchia delle classi
 Il distruttore di una classe derivata neon
deve chiamare il costruttore della classe
base, ma deve occuparsi delle risorse
aggiuntive impegnate dalla classe
derivata

Esempio: costruttori e
distruttori
#include <iostream>
using namespace std;
class base {
public:
base() { cout << "Costruzione di base\n"; }
~base() { cout << "Distruzione di base\n"; }
};
class derived1 : public base {
public:
derived1() { cout << "Costruzione di derived1\n"; }
~derived1() { cout << "Distruzione di derived1\n"; }
};
class derived2 : public derived1 {
public:
derived2() { cout << "Costruzione di derived2\n"; }
~derived2() { cout << "Distruzione di derived2\n"; }
};
Esempio: costruttori e
distruttori
main()
{
derived2 ob;
return 0;
}

Output:
Costruzione
Costruzione
Costruzione
Distruzione
Distruzione
Distruzione
di
di
di
di
di
di
base
derived1
derived2
derived2
derived1
base
I costruttori vengono chiamati in ordine di derivazione, i
distruttori in ordine inverso.
Visibilità della derivazione

Derivazione pubblica




Derivazione protetta


Dovrebbe valere il principio di sostituzione di Liskov
I membri pubblici e protetti della classe base rimangono
tali nella classe derivata
Posso usare la classe derivata al posto della classe base
I membri pubblici della classe base vengono resi protetti
nella classe derivata
Derivazione privata

I membri pubblici e protetti della classe base vengono
resi privati nella classe derivata
La derivazione non pubblica non è una relazione “isa”, ma un dettagli implementativo
(sostanzialmente equivalente alla composizione,
salvo che per la ridefinizione dei membri)
Livelli di accesso nella classe
derivata
Classe
base
Classe
derivata
public
public
Classe
derivata
protected
protected
Classe
derivata
private
private
Membro
protetto
protected
protected
private
Membro
privato
Non
visibile
Non
visibile
Non
visibile
Membro
pubblico
Ridefinizione dei membri

Ridefinire un membro della classe
base nella classe derivata permette di
associare un diverso dato o metodo
allo nome già utilizzato nella classe
base

Vale anche per la ridefinizione dei
membri il principio di Liskov
 L’output
di un membro è vincolato a
rispettare gli stessi requisiti della classe base
 I prerequisiti richiesti alla classe base sono
almeno altrettanto vincolanti di quelli su una
classe derivata
Ereditarietà multipla

L’ereditarietà multipla consente ad
una classe di essere derivata da più
classi base

In presenza di ereditarietà multipla un
oggetto della classe derivata eredita tutti
i membri delle classi di base
Risoluzione delle ambiguità

Un membro della classe derivata nasconde tutti i
membri con lo stesso nome delle classi base


Se più classi base hanno membri con lo stesso nome, si
genera un’ambiguità che causa un errore di compilazione
Occorre risolvere l’ambiguità con l’operatore di scope ::
class date {
// …
public:
string show();
}
class time {
// …
public:
string show();
}
class datetime : public date, public time {
// …
}
datetime dt;
Dt.date::show();
Polimorfismo

Un’interfaccia, molti metodi


Si può creare un’interfaccia generica per un
gruppo di attività collegate
Il polimorfismo è il meccanismo che
permette di usare una classe derivata al
posto di una classe base


Uno stesso metodo ha effetti differenti a
seconda del tipo dell’oggetto su cui è chiamata
Le tecniche che consentono il polimorfismo
sono il binding dinamico e le funzioni virtuali
Polimorfismo

Un riferimento o puntatore ad un oggetto è
polimorfo se può essere riferito a runtime ad
oggetti diversi




Il tipo statico è la classe dichiarata del riferimento
Il tipo dinamico è la classe dell’oggetto dinamicamente
riferito
Non si può avere comportamento polimorfo quando un
tipo è manipolato direttamente
La chiamata di un metodo attraverso un
riferimento polimorfico comporta la decisione a
run-time di quale metodo chiamare (binding
dinamico)

Il compilatore alloca (solo per gli oggetti polimorfi) uno
spazio per memorizzare il tipo dinamico dell’oggetto
Binding dinamico

Il binding è il collegamento della
chiamata di una funzione al corpo
della funzione chiamata

Early binding
 Il
collegamento viene effettuato dal
compilatore o dal linker

Late binding
 Il
collegamento viene eseguito
dinamicamente a runtime

Il linguaggio permette di conoscere il tipo
dinamico dell’oggetto e di chiamare la funzione
membro appropriata
Riepilogo delle relazioni tra
oggetti

Ad un puntatore alla classe base è possibile
assegnare oggetti della classe base o di
classi derivate


Se i membri sono virtuali, viene usato il
membro del tipo dinamico corrispondente
Non è possibile accedere ai membri delle classi
derivate


Un puntatore alla classe base può accedere ai metodi
della classe derivata solo operando esplicitamente un
cast (downcasting)
Ad un puntatore ad una classe derivata
non è possibile assegnare l’indirizzo di un
oggetto della classe base

Un oggetto derivato è un oggetto base, ma un
oggetto base non è un oggetto derivato
Funzioni virtuali

Una funzione virtuale di una classe base può
essere ridefinita dalle classi derivate

Class Impiegato {
public:
virtual void saluta() const { cout <<
“ciao”; }
}
class Manager {
public:
void saluta() const { cout <<
“Buongiorno”; }
}

L’uso della parola chiave virtual nella
dichiarazione attiva il late binding del metodo


Il late binding avviene solo quando la classe base ha
delle funzioni virtuali
Le funzioni virtuali devono essere definite anche nella
classe base
Funzioni virtuali

Una funzione dichiarata virtuale nella classe
base è virtuale per tutte le classi derivate


Una classe che contiene almeno una funzione
virtuale è polimorfa
L’operazione consistente nel ridefinire il metodo
virtuale nella classe derivata è detta overriding


Le funzioni nelle classi derivate con lo stesso
prototipo della funzione virtuale base verranno
chiamate attraverso il late binding
Vale comunque l’ereditarietà per le funzioni
virtuali, dunque se non vengono ridefinite nella
classe derivata viene chiamato il metodo della
classe base
Puntatore alla classe base e
oggetto della classe derivata

Se un puntatore al tipo della classe base
viene usato per accedere ad un oggetto
della classe derivata

Se non si tratta di metodi virtuali, viene
chiamato il membro della classe base



La decisione è compiuta staticamente sulla base del
tipo del puntatore
La funzione accede ai membri della classe base
Se si tratta di funzioni virtuali si accede ai
membri della classe derivata

È il tipo dell’oggetto ha determinare a quali membri
accedere
Funzioni virtuali pure


Una classe si può scrivere anche solo per
consentire di derivarne altre classi, anche
se non sarà mai invocata
Una funzione virtuale pura è una funzione
virtuale che non ha definizione nella sua
classe base




Definire il prototipo della funzione senza
scriverne il corpo
La funzione virtuale pura viene definita
scrivendo =0 al posto del corpo
Non si può istanziare una classe contente
funzioni virtuali pure!
La definizione di una funzione virtuale pura
costringe tutte le derivate (se vogliono poter
essere istanziate) a definire la propria versione
Classi astratte

Una classe è astratta se non fornisce le
implementazioni di tutti i propri membri



Una classe astratta contiene almeno una
funzione virtuale pura
Una classe astratta non può essere
istanziata
Le classi astratte servono come punto di
partenza per la definizione di altre classi


Interfacce
Sottoinsieme comune di comportamenti
Distruttori virtuali
Se si allocano dinamicamente oggetti
e si deallocano con delete,
 e si sta usando un puntatore alla
classe base per riferirsi ad un oggetto
della classe derivata

Viene chiamato il distruttore della classe
base…
 Occorre rendere virtual il distruttore

Classi polimorfe solitamente hanno
distruttori virtuali.
Un costruttore non può essere virtuale!
Scarica

Lezione 6