Una caratteristica essenziale della programmazione ad oggetti consiste nella possibilità di definire una gerarchia di tipi.
Un concetto non esiste come entità a sè stante bensì coesiste con altri concetti collegati da relazioni che contribuiscono in maniera determinante a chiarirne il significato (ed aumentarne la potenza).
In C++ le classi rappresentano concetti: la nozione di classe derivata (con gli associati meccanismi del linguaggio) è il mezzo impiegato per esprimere relazioni gerarchiche, ovvero per catturare aspetti comuni tra classi.
Consideriamo un programma per la gestione del personale di un’azienda. Un’ipotetica struttura dati potrebbe essere:
class Impiegato {
public: Impiegato * next;
char * nome;short reparto;
int salario;
//...
};
class Manager {
Impiegato * gruppo; //persone dirette
public:
Impiegato imp; // record dati
short livello
//...
}
Un manager è anche un impiegato, ma per il compilatore un puntatore a un manager (Manager*
) non è un puntatore a un impiegato (Impiegato*
). Non è possibile inserire un manager in una lista di impiegati (a meno di scrivere codice speciale).
L’approccio corretto consite nel far sì che un manager sia un impiegato con “informazioni” aggiuntive.
class Manager: public Impiegato {
Impiegato * gruppo; //persone dirette
public: short livello
//...
};
La classe Manager
è derivata dalla classe Impiegato
che ne è classe base.
Poichè la classe Manager
ha come classe base pubblica Impiegato
, un putatore Manager*
può comparire al posto di un puntatore Impiegato*
, ma non viceversa
void f(void) {
Manager* m;
m = new Manager
Impiegato* i;
i = new Impiegato;
Impiegato* ilist;
ilist = m; //metti m nella lista
m.next = i; //aggiungi i
//...
};
Se una classe A
ha una classe base pubblica B
, allora un puntatore A*
può essere assegnato a un variabile di tipo B*
.
Alcune questioni importanti
Modifichiamo l’esempio precedente