Una delle caratteristiche piรน interessanti del linguaggioย C++ย รจ la possibilitร di servirsi del meccanismo del polimorfismo, che garantisce un sensibile miglioramento nella semplicitร e soprattutto nella flessibilitร della programmazione.
Vedremo infatti come, in un qualunque contesto operativo, il polimorfismo offra la possibilitร di definire i tipi di dati trattati dal programma (a patto che facciano riferimento ad una base comune) non a priori in fase di programmazione, ma successivamente, ed in modo del tutto indipendente, all’atto di ciascuna esecuzione.
Per utilizzare al meglio tale meccanismo, facciamo un breve ripasso degli strumenti operativi necessari: in particolare la classe e la derivazione tra classi.
Creare una classe in C++
Le classi costituiscono uno degli strumenti piรน utili per la programmazione in diversi linguaggi, tra cui il C++. Per classe si intende un insieme di dati (detti dati membro) propri della classe e di funzioni (dette metodi) per gestire tali dati.
Il codice di un’ipotetica classe di prova di nome Classe_Prova risulterebbe simile a questo:
class Classe_Prova
{
// dati membro
// metodi
};
Si puรฒ pensare ad una classe come alla rappresentazione di una categoria, ovvero di un insieme di oggetti o di individui accomunati da caratteristiche simili. Ad esempio, รจ possibile realizzare una classe casa, una classe impiegato o una classe studente, ciascuna contraddistinta da particolari dati e metodi.
L’analogia tra lo strumento di programmazione rappresentato dalla classe e l’idea di categoria logica, di insieme di oggetti o soggetti simili, puรฒ essere ulteriormente estesa.
Classi base e classi derivate in C++
Osserviamo infatti che tra insiemi diversi possono esistere particolari relazioni. L’insieme delle abitazioni, ad esempio, comprende l’insieme degli appartamenti, che ne รจ sottoinsieme. Si puรฒ dire dunque che ogni appartamento รจ anche una casa.
Si identifica in questo modo una relazione di tipo “is-a” (letteralmente “รจ un/una”). Tale rapporto tra le due categorie trova una corrispondenza immediata a livello di programmazione con le definizioni di classi base e classi derivate.
Il processo di derivazione consente infatti alla classe derivata di ottenere tutti i dati e i metodi della classe base, potendone inoltre aggiungere di propri, piรน specifici, che non sono appartenenti alla classe base.
La relazione infatti non รจ simmetrica: si puรฒ dire, ad esempio, che ogni appartamento sia un’abitazione, ma non che ogni abitazione sia un appartamento.
Impostare il codice di una classe base e della relativa classe derivata (in questo caso proprio Abitazione e Appartamento) risulta piuttosto semplice:
class Abitazione
{
// dati membro
// metodi
};
class Appartamento : public Abitazione
{
// dati membro specifici della classe Appartamento
// metodi specifici della classe Appartamento
};
Si ha inoltre a disposizione la possibilitร di derivare piรน classi da un’unica classe comune (ad esempio Appartamento e Villa possono derivare da Abitazione), oppure, caratteristica decisamente peculiare del C++, non comune neppure a linguaggi diffusamente utilizzati come Java, una classe derivata puรฒ derivare da piรน classi base.
Ad esempio, la classe Avvocato potrebbe derivare sia da Persona che da Libero Professionista, considerando che riassume le caratteristiche di entrambi i gruppi (il problema della derivazione di classi e della derivazione o ereditarietร multipla sono trattati piรน diffusamente in un altro articolo).
Utilizzo del polimorfismo in C++
Passiamo ora, sulla base del concetto di classe e della comprensione della pratica di derivazione di classi, ad esaminare nel dettaglio il meccanismo del polimorfismo.
Immaginiamo che siano state programmate le classi Abitazione, Appartamento e Villa giร citate precedentemente. Tali classi serviranno all’interno del programma principale per creare oggetti (ovvero elementi specifici della classe, ad esempio un determinato appartamento).
Appartamento *app = new Appartamento();
Abitazione *abitaz = new Abitazione();
In questo caso, si creano rispettivamente un nuovo oggetto di tipo Appartamento di nome app e un nuovo oggetto di tipo Abitazione di nome abitaz. Tramite il polimorfismo รจ possibile anche utilizzare un’istruzione di questo tipo:
Abitazione *ab = new Appartamento();
In questo caso viene creato un oggetto di tipo Appartamento di nome ab, nonostante il puntatore faccia riferimento alla classe base Abitazione e non in modo specifico alla classe Appartamento (che deriva perรฒ da Abitazione).
Il polimorfismo garantisce dunque, in sostanza, la possibilitร di creare un oggetto di classe derivata anche tramite un riferimento (o puntatore) ad oggetto di classe base.
Tale possibilitร comporta una semplicitร , ma soprattutto una flessibilitร nella scrittura del codice decisamente maggiore. Immaginiamo ad esempio di voler scrivere un programma in grado di costruire un archivio di dati riguardanti abitazioni generiche, sia appartamenti che ville.
Non ci รจ possibile conoscere il numero esatto di appartamenti e ville all’atto della scrittura del programma, che deve invece potersi adattare ai dati forniti in input dall’utente durante l’esecuzione.
Il problema puรฒ essere semplicemente risolto tramite il polimorfismo. Dichiariamo infatti un puntatore ad un oggetto di classe base (in questo caso Abitazione) che verrร di volta in volta, secondo le richieste dell’utente, associato ad un oggetto di tipo Appartamento o ad un oggetto di tipo Villa.
Ecco un piccolo programma di esempio, che si serve delle funzioni cin e cout (presenti nella libreria iostream, che va richiamata per mezzo della parola chiave #include) per gestire rispettivamente l’input e l’output dei dati rispetto all’utente. Se l’utilizzatore del programma digita “1” come comando, viene creato un oggetto di tipo Appartamento, se digita “2” un oggetto di tipo Villa:
#include <iostream>
using namespace std;
int main() {
int valore_scelta;
Abitazione *ab;
cout << "1-> Appartamento, 2-> Villa\n";
cin >> valore_scelta;
if (valore_scelta == 1)
ab = new Appartamento();
else if (valore_scelta == 2)
ab = new Villa();
// Utilizzo dell'oggetto creato
// ab->metodi_specifici();
return 0;
}
Questo codice illustra come sfruttare il polimorfismo per creare oggetti di classi derivate a runtime, adattando l’esecuzione del programma alle scelte dell’utente. ร fondamentale ricordare che, per evitare perdite di memoria, รจ importante deallocare gli oggetti creati dinamicamente utilizzando delete quando non sono piรน necessari.
In conclusione, il polimorfismo in C++ offre una straordinaria flessibilitร e potenza, consentendo agli sviluppatori di scrivere codice piรน generico e riutilizzabile, semplificando notevolmente la gestione della complessitร nei software moderni.