4. Ereditarietà



4.1 L'albero

Si è già accennato nel cap.1 ad una delle principali novità introdotte dal linguaggio C++: l'Ereditarietà. Con un esempio tratto dal mondo animale, è stato evidenziato come i concetti possano essere organizzati in modo gerarchico, mediante un processo di derivazione.

Vediamo un altro esempio: consideriamo il concetto di Persona.
Esso avrà degli attributi come Nome, Età, Sesso tanto per citarne alcuni. Da tale entità possiamo derivarne molte altre: ad esempio, una Persona può essere Studente, Lavoratore o Pensionato. Ciascuna di queste entità eredita le caratteristiche dell'entità Persona (ha cioè un Nome, un'Età, un Sesso) ed in più ne aggiunge altre: per esempio, un Lavoratore avrà un'Anzianità di Lavoro, una Sede di lavoro, etc.
A sua volta dall'entità Lavoratore possiamo derivarne altre: infatti, un Lavoratore può essere Impiegato, Professionista, Artigiano, e così via il procedimento logico potrebbe continuare.
Per rappresentare graficamente tale processo di derivazione, è utile ricorrere ad una struttura ad Albero come in Fig. 4.1, proprio come avviene per l'Albero Genealogico.

Persona
Fig. 4.1 Albero delle classi derivate da Persona.

4.2 La sintassi in C++

Trasferendo tutto ciò alla programmazione, con l'Ereditarietà è possibile rappresentare in modo efficiente dati e funzioni, evitando ridondanze e ottimizzando la scrittura dei programmi, mediante una riusabilità del codice.
In C++ l'esempio precedente può essere implementato nel modo seguente:

#define MAXCHAR 15

class Persona
{
  char  Nome[MAXCHAR];
  char  Sesso;
  short Eta;

  public:
  Persona();
  ~Persona();
  void MostraDati();
}

class Lavoratore : public Persona
{
  short Anzianità;
  char Sede[MAXCHAR];

  public:
  Lavoratore();
  ~Lavoratore();

}
Il codice delle funzioni membro è stato omesso perchè distoglierebbe l'attenzione dalla sintassi usata per realizzare la derivazione.
Nell'esempio precedente la classe Persona viene anche detta Classe base, poichè a partire da essa sono derivate le altre classi, dette Classi Derivate.
La classe Lavoratore è derivata della classe base: per indicare ciò, la sintassi prevede l'operatore ":" subito dopo il nome della classe, seguito dal nome della classe base. In generale si scrive:
class nome_classeDerivata : etichetta nome_classeBase
{
  codice
}
La classe Lavoratore eredita i dati privati della classe base Persona, ovvero le variabili Nome, Sesso, Età, aggiungendone nuovi.

L'etichetta può essere public, protected o private. L'etichetta protected introduce un ulteriore livello nel meccanismo di sicurezza del C++, intermedio fra private e public: in effetti, tale etichetta ha un senso solo se la classe in cui è presente sarà successivamente derivata, ossia all'interno della stessa classe i membri private e protected hanno lo stesso livello di protezione. Il loro comportamento si differenzia solo nei confronti di eventuali classi derivate dove i protetti della classe base sono accessibili mentre i privati no.
A seconda del valore dell'etichetta nella classe derivata, si ha un diverso comportamento verso i dati membro della classe base.
Si possono presentare i seguenti casi:

  1. Derivazione di tipo public
  2. Derivazione di tipo protected
  3. Derivazione di tipo private
Nel caso 1 i membri ereditati conservano il livello di protezione che possiedono nella classe base (ossia i membri privati continuano ad essere privati, e così via).
Nel caso 2 tutti i membri della classe base assumono nella nuova classe la veste di membri protetti.
Nel caso 3 tutti i membri della classe base diventano privati nella classe derivata. Questa è la derivazione di default.
Dunque, in generale, i membri della classe base all'interno della classe derivata o conservano gli stessi diritti di accesso o hanno diritti più restrittivi.

4.3 L'ereditarietà multipla

Finora è stato preso in esame il caso in cui, a partire da una classe, sia possibile derivare una o più classi, ognuna delle quali discende, quindi da una sola classe.
Il C++ consente anche un tipo di derivazione in cui una classe deriva da più classi. Ad esempio, un Medico può essere sia Impiegato (in Ospedale) che Professionista, secondo lo schema seguente:


Fig 4.2 Un esempio di ereditarietà multipla.

Dal punto di vista sintattico, si scrive:

class Impiegato
{
  . . . .
}

class Professionista
{
 . . . .
}

class Medico : public Impiegato,protected Professionista
{
 . . . .
}

dove ogni classe base può essere derivata con differente modalità.
Non in tutti i linguaggi OOP è previsto il supporto dell'ereditarietà multipla (es. Java).

4.4 Ereditarietà e Costruttori

Quando viene istanziato un oggetto di una classe derivata, viene automaticamente richiamato prima il costruttore di default delle eventuali classi base e poi il costruttore della classe derivata in esame.
Il costruttore di una classe derivata può invocare costruttori delle classi base diversi da quelli di default, esplicitando tale chiamata nella riga di codice relativa al costruttore della classe derivata.
Vediamo un esempio:

class X
{
 . . .
}

class Y:public X
{
 . . .
}

Y OggettoY;
In tal caso, è invocato prima il costruttore di default della classe base, ossia X(), e poi Y().
Nel caso di ereditarietà multipla, si ha:
class Y:public X,public Z
{
 . . .
}
In tal caso, sono invocati dapprima i costruttori di default delle classi base, nell'ordine in cui esse sono state dichiarate, cioè X(), Z() e infine Y().
Una soluzione alternativa può essere:
class Y:public X,public Z
{
 . . .
}

Y::Y(parametri):X(parametri),Z(parametri)
{
 . . .
}
I costruttori sono invocati nell'ordine specificato nella linea di inizializzazione.
Con questa tecnica è possibile inizializzare secondo le proprie esigenze i dati membro della classe base, ereditati dalla classe derivata.


dx
Precedente
back
Home page
dx
Successivo