back to top

Il polimorfismo in C++

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.

Pubblicità

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.

Altri contenuti interessanti

Pubblicità

Leggi anche...

Vibe Coding: cos’è, come funziona e quali sono i migliori strumenti AI per programmare

Immagina di poter scrivere software senza dover digitare una...

I migliori libri per imparare a programmare in Python

Imparare a programmare in Python è un passo fondamentale...

Il file manifest.json: cos’è e a cosa serve

Il file manifest.json è un componente chiave nelle applicazioni web moderne,...

Java: cos’è e a cosa serve l’operatore modulo (%)

In Java, l'operatore modulo è rappresentato dal simbolo "%"...

Radice quadrata in C: vediamo come calcolarla in diversi modi

La radice quadrata è un'operazione matematica piuttosto comune (in...

Sperimentare la sequenza di Collatz in C++

Vediamo come verificare la congettura di Collatz con C++....
Pubblicità