Ereditarietà Base

Con l'ereditarietà una classe derivata acquisisce attributi e metodi della classe base, potendo aggiungerne di nuovi o modificarne il comportamento.

C++Classe base Animale → classi derivate
#include <iostream>
#include <string>
using namespace std;

// Classe BASE
class Animale {
protected:                     // Accessibile alle classi derivate
    string nome;
    int    eta;

public:
    Animale(const string &n, int e) : nome(n), eta(e) {}

    void respira() const { cout << nome << " sta respirando.\n"; }

    // virtual: permette l'override nelle classi derivate
    virtual void verso() const { cout << nome << " fa un verso generico.\n"; }
    virtual void info()  const {
        cout << "Animale: " << nome << ", età " << eta << "\n";
    }

    // Distruttore SEMPRE virtuale in classi base con funzioni virtuali!
    virtual ~Animale() {}

    string getNome() const { return nome; }
};

// Classe DERIVATA
class Cane : public Animale {
    string razza;

public:
    Cane(const string &n, int e, const string &r)
        : Animale(n, e), razza(r) {}

    // Override di verso()
    void verso() const override {
        cout << nome << " (Cane) fa: Bau Bau!\n";
    }

    // Override di info() con chiamata al metodo base
    void info() const override {
        Animale::info();           // Chiama il metodo della classe base
        cout << "  Razza: " << razza << "\n";
    }

    void riporta() const { cout << nome << " riporta la pallina!\n"; }
};

class Gatto : public Animale {
public:
    Gatto(const string &n, int e) : Animale(n, e) {}

    void verso() const override { cout << nome << " (Gatto) fa: Miao!\n"; }
    void graffia() const { cout << nome << " graffia!\n"; }
};

int main() {
    Cane  fido("Fido",   3, "Labrador");
    Gatto luna("Luna",   2);
    Animale generico("Bestia", 1);

    fido.info();
    fido.verso();
    fido.respira();         // Ereditato da Animale
    fido.riporta();         // Solo in Cane

    cout << "\n";
    luna.verso();
    luna.graffia();

    return 0;
}

Polimorfismo con Puntatori

Il vero potere del polimorfismo si vede quando usi puntatori alla classe base che puntano a oggetti di classi derivate.

C++Polimorfismo a runtime (dynamic dispatch)
#include <iostream>
#include <vector>
#include <memory>    // smart pointer: unique_ptr
using namespace std;

class Forma {
public:
    virtual double area()      const = 0;  // = 0 → funzione PURA (astratta)
    virtual double perimetro() const = 0;
    virtual string nome()      const = 0;

    void stampa() const {
        cout << nome() << ": area=" << area()
             << ", perim=" << perimetro() << "\n";
    }

    virtual ~Forma() {}
};

// Cerchio
class Cerchio : public Forma {
    double r;
public:
    Cerchio(double r) : r(r) {}
    double area()      const override { return 3.14159 * r * r; }
    double perimetro() const override { return 2 * 3.14159 * r; }
    string nome()      const override { return "Cerchio(r=" + to_string(r) + ")"; }
};

// Rettangolo
class Rettangolo : public Forma {
    double l, h;
public:
    Rettangolo(double l, double h) : l(l), h(h) {}
    double area()      const override { return l * h; }
    double perimetro() const override { return 2*(l+h); }
    string nome()      const override { return "Rettangolo(" + to_string(l) + "x" + to_string(h) + ")"; }
};

// Triangolo (area con formula di Erone)
class Triangolo : public Forma {
    double a, b, c;
public:
    Triangolo(double a, double b, double c) : a(a), b(b), c(c) {}
    double perimetro() const override { return a+b+c; }
    double area()      const override {
        double s = perimetro() / 2;
        return sqrt(s*(s-a)*(s-b)*(s-c));
    }
    string nome()      const override { return "Triangolo(" + to_string(a) + "," + to_string(b) + "," + to_string(c) + ")"; }
};

int main() {
    // Vettore di smart pointer a Forma → polimorfismo!
    vector<unique_ptr<Forma>> forme;
    forme.push_back(make_unique<Cerchio>(5.0));
    forme.push_back(make_unique<Rettangolo>(4.0, 6.0));
    forme.push_back(make_unique<Triangolo>(3.0, 4.0, 5.0));
    forme.push_back(make_unique<Cerchio>(2.5));

    double area_totale = 0;
    for (const auto &f : forme) {
        f->stampa();              // Chiamata polimorfica!
        area_totale += f->area();
    }

    cout << "\nArea totale di tutte le forme: " << area_totale << "\n";

    // dynamic_cast per verificare il tipo a runtime
    for (const auto &f : forme) {
        if (auto *c = dynamic_cast<Cerchio*>(f.get())) {
            cout << "Trovato un cerchio!\n";
        }
    }

    return 0;
}
Classi Astratte Una classe con almeno una pure virtual function (= 0) è astratta: non può essere istanziata direttamente. Serve come interfaccia (contratto) che le classi derivate devono rispettare.

Interfacce e Ereditarietà Multipla

C++Interfacce con classi astratte pure
#include <iostream>
#include <string>
using namespace std;

// "Interfacce" in C++ — classi con solo virtual pure
class Stampabile {
public:
    virtual void stampa() const = 0;
    virtual ~Stampabile() {}
};

class Serializzabile {
public:
    virtual string toJSON() const = 0;
    virtual ~Serializzabile() {}
};

// Ereditarietà multipla
class Prodotto : public Stampabile, public Serializzabile {
    string nome;
    double prezzo;
    int    quantita;

public:
    Prodotto(const string &n, double p, int q)
        : nome(n), prezzo(p), quantita(q) {}

    void stampa() const override {
        cout << "[" << nome << "] EUR " << prezzo << " x" << quantita << "\n";
    }

    string toJSON() const override {
        return "{\"nome\":\"" + nome + "\","
               "\"prezzo\":" + to_string(prezzo) + ","
               "\"quantita\":" + to_string(quantita) + "}";
    }

    double valore() const { return prezzo * quantita; }
};

int main() {
    Prodotto laptop("Laptop", 899.99, 3);
    laptop.stampa();
    cout << laptop.toJSON() << "\n";
    cout << "Valore totale: " << laptop.valore() << "\n";

    return 0;
}
🏋️ Esercizi
Padroneggia ereditarietà e polimorfismo.
1
Gerarchia di veicoli
FacileEreditarietà

Crea la gerarchia: Veicolo (base: targa, velocità massima) → Auto (num porte) → Moto (tipo: sportiva/touring) → Camion (portata in tonnellate). Ogni classe sovrascrive descrizione(). Usa un vettore di puntatori a Veicolo.

C++
#include <iostream>
#include <vector>
#include <memory>
#include <string>
using namespace std;

class Veicolo {
protected:
    string targa;
    int    vmax;
public:
    Veicolo(const string &t, int v) : targa(t), vmax(v) {}
    virtual void descrizione() const {
        cout << "Veicolo " << targa << " (max " << vmax << " km/h)";
    }
    virtual ~Veicolo() {}
};

class Auto : public Veicolo {
    int porte;
public:
    Auto(const string &t, int v, int p) : Veicolo(t,v), porte(p) {}
    void descrizione() const override {
        Veicolo::descrizione();
        cout << " | Auto " << porte << " porte\n";
    }
};

class Moto : public Veicolo {
    string tipo;
public:
    Moto(const string &t, int v, const string &tipo) : Veicolo(t,v), tipo(tipo) {}
    void descrizione() const override {
        Veicolo::descrizione();
        cout << " | Moto " << tipo << "\n";
    }
};

class Camion : public Veicolo {
    double portata;
public:
    Camion(const string &t, int v, double p) : Veicolo(t,v), portata(p) {}
    void descrizione() const override {
        Veicolo::descrizione();
        cout << " | Camion " << portata << "t\n";
    }
};

int main() {
    vector<unique_ptr<Veicolo>> parco;
    parco.push_back(make_unique<Auto>("AB123CD", 180, 5));
    parco.push_back(make_unique<Moto>("XZ789AB", 220, "sportiva"));
    parco.push_back(make_unique<Camion>("CA001ZZ", 120, 18.5));

    for (const auto &v : parco) v->descrizione();
}
2
Sistema di notifiche
MedioClassi astratte

Crea un'interfaccia astratta Notificatore con metodo puro invia(messaggio). Implementa: EmailNotificatore, SMSNotificatore, PushNotificatore. Ogni classe simula l'invio stampando un messaggio diverso. Crea una funzione notifica_tutti() che prenda un vettore di puntatori a Notificatore.

C++
#include <iostream>
#include <vector>
#include <string>
using namespace std;

class Notificatore {
public:
    virtual void invia(const string &msg) const = 0;
    virtual string tipo() const = 0;
    virtual ~Notificatore() {}
};

class EmailNotificatore : public Notificatore {
    string email;
public:
    EmailNotificatore(const string &e) : email(e) {}
    void invia(const string &msg) const override {
        cout << "[EMAIL → " << email << "] " << msg << "\n";
    }
    string tipo() const override { return "Email"; }
};

class SMSNotificatore : public Notificatore {
    string numero;
public:
    SMSNotificatore(const string &n) : numero(n) {}
    void invia(const string &msg) const override {
        cout << "[SMS → " << numero << "] " << msg.substr(0, 160) << "\n";
    }
    string tipo() const override { return "SMS"; }
};

class PushNotificatore : public Notificatore {
    string device;
public:
    PushNotificatore(const string &d) : device(d) {}
    void invia(const string &msg) const override {
        cout << "[PUSH → " << device << "] 🔔 " << msg << "\n";
    }
    string tipo() const override { return "Push"; }
};

void notifica_tutti(const vector<Notificatore*> &lista, const string &msg) {
    cout << "\nInvio a " << lista.size() << " destinatari:\n";
    for (const auto *n : lista) n->invia(msg);
}

int main() {
    EmailNotificatore email("mario@example.com");
    SMSNotificatore   sms("+39 333 1234567");
    PushNotificatore  push("iPhone-Mario");

    vector<Notificatore*> lista = {&email, &sms, &push};
    notifica_tutti(lista, "Il tuo ordine è stato spedito!");
}
3
Gioco di ruolo base
DifficileOOP / polimorfismo completo

Crea un mini-RPG: classe astratta Personaggio (nome, hp, attacco, difesa) con metodo virtuale puro abilita_speciale(). Implementa: Guerriero (colpo doppio), Mago (magia AoE), Ladro (critico). Implementa un sistema di combattimento a turni tra due personaggi scelti dall'utente.

C++
#include <iostream>
#include <memory>
#include <cstdlib>
#include <ctime>
using namespace std;

class Personaggio {
protected:
    string nome;
    int hp, maxHp, attacco, difesa;

public:
    Personaggio(const string &n, int hp, int atk, int def)
        : nome(n), hp(hp), maxHp(hp), attacco(atk), difesa(def) {}

    virtual void abilita_speciale(Personaggio &nemico) = 0;
    virtual string classe() const = 0;
    virtual ~Personaggio() {}

    void subisci_danno(int danno) {
        int reale = max(0, danno - difesa);
        hp = max(0, hp - reale);
        cout << "  " << nome << " subisce " << reale << " danni (HP: " << hp << "/" << maxHp << ")\n";
    }

    bool isVivo() const { return hp > 0; }
    string getNome() const { return nome; }

    void attacca(Personaggio &nemico) {
        cout << nome << " attacca " << nemico.nome << " per " << attacco << "!\n";
        nemico.subisci_danno(attacco);
    }

    void stampa() const {
        cout << "[" << classe() << "] " << nome
             << " HP:" << hp << "/" << maxHp
             << " ATK:" << attacco << " DEF:" << difesa << "\n";
    }
};

class Guerriero : public Personaggio {
public:
    Guerriero(const string &n) : Personaggio(n, 120, 18, 8) {}
    string classe() const override { return "Guerriero"; }
    void abilita_speciale(Personaggio &nemico) override {
        cout << nome << " usa COLPO DOPPIO!\n";
        nemico.subisci_danno(attacco);
        nemico.subisci_danno(attacco);
    }
};

class Mago : public Personaggio {
public:
    Mago(const string &n) : Personaggio(n, 80, 28, 3) {}
    string classe() const override { return "Mago"; }
    void abilita_speciale(Personaggio &nemico) override {
        int magia = attacco * 2;
        cout << nome << " lancia PALLA DI FUOCO per " << magia << "!\n";
        nemico.subisci_danno(magia);
    }
};

class Ladro : public Personaggio {
public:
    Ladro(const string &n) : Personaggio(n, 90, 22, 5) {}
    string classe() const override { return "Ladro"; }
    void abilita_speciale(Personaggio &nemico) override {
        if (rand() % 2) {
            cout << nome << " CRITICO! Danno x3!\n";
            nemico.subisci_danno(attacco * 3);
        } else {
            cout << nome << " manca il colpo critico!\n";
        }
    }
};

int main() {
    srand(time(0));

    unique_ptr<Personaggio> p1 = make_unique<Guerriero>("Arthas");
    unique_ptr<Personaggio> p2 = make_unique<Mago>("Merlin");

    cout << "=== COMBATTIMENTO ===\n";
    p1->stampa(); p2->stampa();

    int turno = 1;
    while (p1->isVivo() && p2->isVivo()) {
        cout << "\n--- Turno " << turno++ << " ---\n";
        if (turno % 3 == 0) { p1->abilita_speciale(*p2); }
        else p1->attacca(*p2);
        if (!p2->isVivo()) break;
        if (turno % 4 == 0) p2->abilita_speciale(*p1);
        else p2->attacca(*p1);
    }

    cout << "\n=== FINE ===\n";
    cout << (p1->isVivo() ? p1->getNome() : p2->getNome()) << " vince!\n";
}