Classi astratte

2 febbraio 2012

Una classe base, definita con funzioni virtuali, serve a stabilire cosa possono fare gli oggetti delle classi derivate. Nel nostro esempio, la classe base veicolo impone ad ogni oggetto (istanza di una classe derivata) di dichiarare quante ruote ha, attraverso la propria funzione numRuote().

In altre parole la classe base fornisce, oltre alle funzioni, anche uno “schema di comportamento” per le classi derivate. Portando all’estremo questa pratica, possiamo creare una classe base con funzioni virtuali senza codice, dette funzioni virtuali pure. Non avendo codice, queste funzioni servono solo da “schema di comportamento” per le classi derivate.

Una classe base con almeno una funzione virtuale pura è detta classe astratta, perché definisce la struttura di una gerarchia di classi, ma non può essere istanziata direttamente. Possiamo dire che una classe astratta è una classe da cui non si possono creare oggetti ed ha solo una funzione di “scheletro” per definire sottoclassi.

Per dichiarare una funzione come virtuale pura, basta aggiungere "= 0" alla dichiarazione nel seguente modo:

virtual void numRuote() = 0;

Riprendiamo l’esempio dei veicoli ma stavolta utilizzando come classe base una classe astratta. Nel codice mettiamo in evidenza il fatto che si può definire un puntatore ad una classe astratta, ma non si può istanziarla!

Come a dire che possiamo immaginare di avere un puntatore ad un veicolo (in astratto), ma poi per valorizzare il puntatore abbiamo bisogno di un veicolo “reale”, ovvero l’istanza di una classe derivata.

#include <iostream.h>
class veicolo
{
  public:
    virtual void numRuote() = 0;
};

class automobile : public veicolo
{
  void numRuote() { cout << "auto appartiene a veicolo ed ha 4 ruote" << endl; }
};

int main()
{
    // Questo NON È permesso (la classe veicolo è astratta)
    /* veicolo *v = new veicolo(); */

    // Questo è Permesso (automobile è una classe normale)
    automobile* c = new automobile();

    // possiamo ottenere dei puntatori di tipo veicolo che puntano classi reali
    veicolo* f = new automobile();

	return 0;
}

A differenza dalle normali funzioni virtuali, le funzioni virtuali pure devono essere ridefinite (implementate) tutte nelle classi derivate (anche con “corpo nullo”, quando non servono). Se una classe derivata non implementa anche una sola funzione virtuale pura della classe base, rimane una classe astratta e non può ancora essere istanziata.

Le classi astratte sono di importanza fondamentale nella programmazione in C++. Esse presentano delle interfacce , senza il vincolo degli aspetti implementativi, che sono invece forniti dalle loro classi derivate.

Una gerarchia di classi, che deriva da una o più classi astratte, può essere costruita in modo “incrementale”, ciò permette il “raffinamento” di un progetto, aggiungendo via via nuove classi senza la necessità di modificare le parti preesistenti.

Le classi astratte sono usate per dichiarare componenti comuni per le classi che ne derivano. Quando si eredita da una classe astratta in una classe, dopo aver definito un suo membro, si può usarlo in tutte le funzioni definite nelle classi figlie.

Vediamo di metter in pratica quello fin qui detto riprendendo l’esempio dei veicoli:

#include <iostream>

using namespace std;

class veicolo
{
  public:
    virtual void numRuote() = 0;
};

class automobile : public veicolo {
  public:
    void numRuote() { cout << "automobile appartiene a veicolo ed ha 4 ruote" << endl; }
};

class furgone : public veicolo {
  public:
    void numRuote () { cout << "furgone ha 4 ruote" << endl; };
};

class moto : public veicolo {
  public:
    void numRuote () { cout << "moto ha 2 ruote" << endl; };
};


int main()
{
    veicolo* veicoliaMotore[3] = { new automobile, new furgone, new moto };

    for(int i = 0; i < 3; i++)
      veicoliaMotore[i]->numRuote();

    return 0;
}

Nell’esempio su proposto la classe veicolo è una classe astratta in quanto definisce una funzione virtuale pura:

virtual void numRuote() = 0;

veicolo ha il solo scopo di riunire le proprietà ( nel caso specifico: numRuote()) comuni all’intera gerarchia di classi derivate.

Il meccanismo delle funzioni virtuali, come mostrato nell’esempio precedente, consente di avere una funzione che ha lo stesso nome su ogni oggetto della gerarchia di classi su cui agisce, ma è implementata in modo diverso e il compilatore sceglierà automaticamente la versione giusta da chiamare.

In virtù del polimorfismo è infatti possibile implementare liste eterogenee di oggetti (il vettore veicoliaMotore ne è un semplice esempio: è un array statico in quanto formato da soli 3 elementi, di oggetti dinamici). Nell’esempio viene richiamata prima la funzione numRuote() di automobile, poi quella di furgone e per ultima quella di moto.

Tutte le lezioni

1 ... 56 57 58

 

 

1 ... 56 57 58

Se vuoi aggiornamenti su Classi astratte inserisci la tua e-mail nel box qui sotto:
Tags:
 
X
Se vuoi aggiornamenti su Classi astratte

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy