Soluzioni: Appendice 2

../_images/linea.png

A2.1-A2.2 struttura di ereditarietà delle forme geometriche

  • esercizio01_02.cpp

// g++ -o Esercizio01_02 esercizio01_02.cpp forma.cc quadrato.cc rettangolo.cc

#include "forma.h"
#include "quadrato.h"
#include "rettangolo.h"

#include <iostream>

using std::cout;
using std::endl;
using std::cin;

/*
 *
 *  Si definisca una semplice struttura di ereditarieta' delle forme geometriche, seguendo l'esempio presentato ad inizio lezione.
 *    - Si aggiunga alla classe forma un membro private (ad esempio un'etichetta) e si verifichi che e' accessibile 
 *        dalle classi derivate solamente tramite un metodo aggiuntivo public o protected della classe base.
 *    - Si verifichi la sequenza delle chiamate dei costruttori e dei distruttori della parte base e derivata delle 
 *        classi al momento della creazione e della distruzione dei diversi oggetti, inserendo in ogni costruttore e 
 *        distruttore un messaggio a schemo.
 *
 *
 *  Si modifichi la struttura precedente aggiungendo la funzione calcola_area in tutta la catena di ereditarieta'.
 *
 *    - Si verifichi il comportamento polimorfico introdotto dal dynamic binding.
 *    - Si verifichi che, eliminando la parola chiave virtual, il dynamic binding non funziona.
 *    - Si verifichi che il dynamic binding avviene in esecuzione e non in fase di compilazione, 
 *        implementando una funzione void stampa_area (forma * input) che stampa a schermo l'area 
 *        di un oggetto di tipo rettangolo o quadrato a seconda della scelta dell'utente.
 *
 */

int main(int argc, char** argv) {

    /*
     *  Esercizio 01
     *
     *  Vengono definite due classi, forma è una classe generale e quadrato eredita da forma.
     *  
     *  La classe 'forma' ha un membro protected (m_area), direttamente accessibile nella classe quadrato, 
     *  e un membro privato (m_nome), NON accessibile nella classe quadrato.
     */
    cout << "Creo un oggetto di tipo forma" << endl;
    forma forma_generica(10);

    cout << "forma_generica" << endl;
    cout << "  nome: " << forma_generica.GetNome() << endl;
    cout << endl;

    cout << "Creo un oggetto di tipo quadrato" << endl;
    quadrato forma_quadrata(5);

    cout << "forma_quadrata" << endl;
    cout << "  nome: " << forma_quadrata.GetNome() << endl;
    cout << endl;

    /*
     *  Esercizio 02
     *
     *      Vengono aggiunti i metodi calcola_area (definito come virtual) e 
     *      calcola_area_non_virtual (non definito come virtual) e si verifica 
     *      che, nel caso di metodo virtual, il dynamic binding funziona
     */

    // Area di forma_generica
    cout << "Calcolo l'area" << endl;
    cout << "forma_generica" << endl;
    double area_forma = forma_generica.calcola_area();
    cout << "  area: " << area_forma << endl;
    cout << endl;

    // Area di forma_quadrata
    cout << "forma_quadrata" << endl;
    double area_quadrato = forma_quadrata.calcola_area();
    cout << "  area: " << area_quadrato << endl;
    cout << endl;

    // Esempio di dynamic binding: usando un puntatore a un oggetto di tipo forma
    // viene chiamato il metodo 'calcola_area' della classe derivata quadrato
    //
    // Bisogna definire forma::calcola_area() come virtual!

    forma* puntatore_quadrato = &forma_quadrata;
    cout << "dynamic binding" << endl;
    double area_quadrato_virtual = puntatore_quadrato->calcola_area();
    cout << endl;

    // Esempio errato di dynamic binding: usando il metodo calcola_area_non_virtual
    // (non definito come virtual) non viene chiamato il metodo corretto
    cout << "dynamic binding errato - metodo non virtual" << endl;
    double area_quadrato_non_virtual = puntatore_quadrato->calcola_area_non_virtual();
    cout << endl;

    // Il dynamic binding ha effetto in fase di esecuzione, non di compilazione
    int risultato = -1;
    while( risultato != 0 && risultato != 1 ) {
        cout << "Inserire 0 per un quadrato (area 400), 1 per un rettangolo (area 150)" << endl;
        cin >> risultato;
        cout << endl;
    }

    // Lego un puntatore a forma a un oggetto di tipo quadrato o rettangolo,
    // a seconda della scelta dell'utente
    forma* forma_dyn;
    if( risultato == 0 ) {
        forma_dyn = new quadrato(20);
    } else if( risultato == 1 ) {
        forma_dyn = new rettangolo(15, 10);
    }

    if( forma_dyn ) {
        double area = forma_dyn->calcola_area();
        cout << "Area : " << area << endl;
        delete forma_dyn;
        cout << endl;
    }
}
  • forma.h

#ifndef forma_h
#define forma_h

#include <string>

class forma {
    public:
        // Costruttore di default
        forma();
        // Costruttore con parametro
        forma(double area = -1);
        // Distruttore
        ~forma();

        // Restituisce il nome
        std::string GetNome();
        // Imposta il nome
        void SetNome( std::string nome );

        // Calcola l'area
        virtual double calcola_area();

        // Calcola l'area - non virtual
        double calcola_area_non_virtual();

    protected:
        /*
         *  I membri protected sono direttamente accessibili alle classi
         *  che ereditano da questa classe
         */

        double m_area;

    private:
        /*
         *  I membri private sono accessibili solo ad oggetti di tipo forma,
         *  non alle classi che ereditano da forma
         */
        std::string m_nome;
};

#endif
  • forma.cc

#include "forma.h"

#include <iostream>

// In alternativa a 'using namespace std;', è possibile importare solo
// alcune funzioni da un namespace
using std::cout;
using std::endl;

/*
 *  Costruttore di default
 */
forma::forma(): m_area(-1), m_nome("forma") {
    cout << "Chiamato il costruttore (default) di forma" << endl;
}

/*
 *  Costruttore con un parametro
 */
forma::forma(double area): m_area(area), m_nome("forma") {
    cout << "Chiamato il costruttore (con parametro) di forma" << endl;
}

/*
 *  Distruttore
 */
forma::~forma() {
    cout << "Chiamato il distruttore di forma" << endl;
}

/*
 *  Restituisce il nome 
 */
std::string forma::GetNome() {
    return m_nome;
}

/*
 *  Imposta il nome
 */
void forma::SetNome(std::string nome) {
    m_nome = nome;
}

/*
 *  Calcola l'area 
 */ 
double forma::calcola_area() {
    cout << "Chiamata la funzione calcola_area di forma" << endl;
    return m_area;
}

/*
 *  Calcola l'area - metodo non virtual
 */
double forma::calcola_area_non_virtual() {
    cout << "Chiamata la funzione calcola_area_non_virtual di forma" << endl;
    return m_area;
}
  • quadrato.h

#ifndef quadrato_h
#define quadarato_h

#include "forma.h"

class quadrato: public forma {
    public:
        // Costruttore di default
        quadrato();
        // Costruttore con parametro
        quadrato(double lato = 1);
        // Distruttore
        ~quadrato();

        // Cambia lato
        void cambia_lato(double lato);

        // Calcola l'area
        double calcola_area();

        // Calcola l'area - metodo non virtual
        double calcola_area_non_virtual();

    private:
        double m_lato;
};

#endif
  • quadrato.cc

#include "quadrato.h"

#include <iostream>

using std::cout;
using std::endl;

/*
 *  Costruttore di default
 */
quadrato::quadrato(): m_lato(1), forma(1) {
    /*
     *  Non posso modificare direttamente il contenuto di m_nome, è un membro privato
     *  di 'forma'. Usare qui m_nome = "quadrato" sarebbe un errore
     *  Usando SetNome(), che è un metodo pubblico, non ci sono errori
     */
    // m_nome = "quadrato";
    SetNome("quadrato");
    cout << "Chiamato il costruttore (default) di quadrato" << endl;
}

/*
 *  Costruttore con parametro
 */
quadrato::quadrato(double lato): m_lato(lato), forma(lato*lato) {
    /*
     *  Non posso modificare direttamente il contenuto di m_nome, è un membro privato
     *  di 'forma'. Usare qui m_nome = "quadrato" sarebbe un errore.
     *  Usando SetNome(), che è un metodo pubblico, non ci sono errori
     */
    // m_nome = "quadrato";
    SetNome("quadrato");
    cout << "Chiamato il costruttore (con parametro) di quadrato" << endl;
}

/*
 *  Distruttore
 */
quadrato::~quadrato() {
    cout << "Chiamato il distruttore di quadrato" << endl;
}

/*
 *  Cambia lato
 */
void quadrato::cambia_lato( double lato ) {
    m_lato = lato;
    // Posso accedere a m_area, membro di forma, perchè è protected
    m_area = m_lato*m_lato;
}

/*
 *  Calcola l'area
 */
double quadrato::calcola_area() {
    cout << "chiamata la funzione calcola_area di quadrato" << endl;
    m_area = m_lato*m_lato;
    return m_area;
}

/*
 *  Calcola l'area - metodo non virtual
 */
double quadrato::calcola_area_non_virtual() {
    cout << "chiamata la funzione calcola_area_non_virtual di quadrato" << endl;
    m_area = m_lato*m_lato;
    return m_area;
}
  • rettangolo.h

#ifndef rettangolo_h
#define quadarato_h

#include "forma.h"

class rettangolo: public forma {
    public:
        // Costruttore di default
        rettangolo();
        // Costruttore con parametro
        rettangolo(double lato_1 = 1, double lato_2 = 1);
        // Distruttore
        ~rettangolo();

        // Cambia lato
        void cambia_lato_1(double lato);
        void cambia_lato_2(double lato);

        // Calcola l'area
        double calcola_area();

        // Calcola l'area - metodo non virtual
        double calcola_area_non_virtual();

    private:
        double m_lato_1;
        double m_lato_2;
};

#endif
  • rettangolo.cc

#include "rettangolo.h"

#include <iostream>

using std::cout;
using std::endl;

/*
 *  Costruttore di default
 */
rettangolo::rettangolo(): m_lato_1(1), m_lato_2(1), forma(1) {
    /*
     *  Non posso modificare direttamente il contenuto di m_nome, è un membro privato
     *  di 'forma'. Usare qui m_nome = "rettangolo" sarebbe un errore
     *  Usando SetNome(), che è un metodo pubblico, non ci sono errori
     */
    // m_nome = "rettangolo";
    SetNome("rettangolo");
    cout << "Chiamato il costruttore (default) di rettangolo" << endl;
}

/*
 *  Costruttore con parametro
 */
rettangolo::rettangolo(double lato_1, double lato_2): 
    m_lato_1(lato_1), m_lato_2(lato_2), forma(lato_1*lato_2) {
    /*
     *  Non posso modificare direttamente il contenuto di m_nome, è un membro privato
     *  di 'forma'. Usare qui m_nome = "rettangolo" sarebbe un errore.
     *  Usando SetNome(), che è un metodo pubblico, non ci sono errori
     */
    // m_nome = "rettangolo";
    SetNome("rettangolo");
    cout << "Chiamato il costruttore (con parametro) di rettangolo" << endl;
}

/*
 *  Distruttore
 */
rettangolo::~rettangolo() {
    cout << "Chiamato il distruttore di rettangolo" << endl;
}

/*
 *  Cambia lato
 */
void rettangolo::cambia_lato_1( double lato_1 ) {
    m_lato_1 = lato_1;
    // Posso accedere a m_area, membro di forma, perchè è protected
    m_area = m_lato_1*m_lato_2;
}

/*
 *  Calcola l'area
 */
double rettangolo::calcola_area() {
    cout << "chiamata la funzione calcola_area di rettangolo" << endl;
    m_area = m_lato_1*m_lato_2;
    return m_area;
}

/*
 *  Calcola l'area - metodo non virtual
 */
double rettangolo::calcola_area_non_virtual() {
    cout << "chiamata la funzione calcola_area_non_virtual di rettangolo" << endl;
    m_area = m_lato_1*m_lato_2;
    return m_area;
}
../_images/linea.png

A2.3-A2.4 struttura di ereditarietà e generazione di numeri casuali

  • esercizio03_04.cpp

// g++ -o Esercizio03_04 esercizio03_04.cpp generatore.cc generatore_TC.cc generatore_IF.cc generatore_LC.cc

#include "generatore.h"
#include "generatore_TC.h"
#include "generatore_IF.h"
#include "generatore_LC.h"

#include <iostream>
#include <map>
#include <string>
#include <ctime>

using std::cout;
using std::endl;

/*
 *  Lezione 8 - Esercizio 03
 *
 *  Si implementino i generatori di numeri casuali scritti durante la Lezione 4 
 *  come oggetti derivati della seguente classe puramente virtuale e si verifichi 
 *  il dynamic binding.
 *
 *      class generatore {
 *          ...
 *      };
 *
 *
 *  Lezione 8 - Esercizio 04
 *
 *  Si inseriscano i generatori precedenti in una std::map e si utilizzi un 
 *  loop sugli elementi della mappa per verificare quanto tempo impieghi 
 *  ciascun generatore, in media, a generare un evento pseudo-casuale.
 */

int main(int argc, char** argv) {

    // Chiave: nome del generatore
    // Valore: puntatore a un oggetto derivante da generatore
    std::map<std::string, generatore*> mappa_generatori;

    int seed = 14741;
    long long int nPoints = 100000;

    // Aggiungo i generatori alla mappa, associandoli al proprio nome
    mappa_generatori["lineare congruenziale"] = new generatore(seed);
    mappa_generatori["try and catch - gaus"] = new generatore_TC(seed, kGAUS, -20, 20);
    mappa_generatori["try and catch - exp"] = new generatore_TC(seed, kEXP, 0, 100);
    mappa_generatori["funzione inversa - exp"] = new generatore_IF(seed, 0, 100);
    mappa_generatori["teorema limite centrale - 100 iterazioni"] = new generatore_LC(seed, 100, -5, 5);
    cout << endl;

    // Itero sulla mappa di generatori
    cout << "Tempo richiesto per generare " << nPoints << " numeri casuali" << endl;
    std::string nome;
    generatore* gen;

    std::map<std::string, generatore*>::const_iterator gen_it;
    for(gen_it = mappa_generatori.begin(); gen_it != mappa_generatori.end(); gen_it++) {
        nome = gen_it->first;
        gen = gen_it->second;

        clock_t startTime = clock();
        for(int i = 0; i < nPoints; i++) {
            double val = gen->generate();
        }
        clock_t endTime = clock();

        cout << nome << " : " << (endTime-startTime)/(static_cast<double>CLOCKS_PER_SEC) << endl;
    }
    cout << endl;

    // Pulizia del contenuto della mappa
    for(gen_it = mappa_generatori.begin(); gen_it != mappa_generatori.end(); gen_it++) {
        delete gen_it->second;
    }
    mappa_generatori.clear();

    return 0;
}
  • generatore.h

#ifndef generatore_h
#define generatore_h

/*
 *  Generatore lineare congruenziale
 */

class generatore {
    public:
        // Costruttore
        generatore(int seed);
        // Distruttore
        ~generatore();

        // Imposta il seed
        void set_seed(int seed);

        // Genera un numero casuale
        virtual double generate();

    protected:
        // Seed
        int m_seed;

        // Costanti per la generazione di numeri casuali
        const long long int A = 214013;
        const long long int C = 2531011;
        const long long int M = 2147483647;

        // Genera un numero casuale nel range tra m_min e m_max
        double rand_range(double min, double max);
};


#endif
  • generatore.cc

#include "generatore.h"

#include <iostream>

using std::cout;
using std::endl;

/*
 *  Costruttore
 */
generatore::generatore(int seed): m_seed(seed) {
    cout << "Chiamato il costruttore del generatore lineare congruenziale" << endl;
}

/*
 *  Distruttore
 */
generatore::~generatore() {
    cout << "Chiamato il distruttore del generatore lineare congruenziale" << endl;
}

/*
 *  Imposta il seed  
 */
void generatore::set_seed(int seed) {
    m_seed = seed;
    cout << "Chiamato il metodo set_seed del generatore lineare congruenziale" << endl;
}

/*
 *  Restituisce il prossimo numero casuale
 */
double generatore::generate() {
    m_seed = (A*m_seed + C) % M;
    return m_seed;
}

/*
 *  Genera un numero casuale in un range [m_min, m_max]
 */
double generatore::rand_range(double min, double max) {
    // Genero un numero casuale dal generatore congruenziale
    double rnd = generatore::generate();

    return min + (max - min) * rnd / static_cast<double> (M);
}
  • generatore_IF.h

#ifndef generatore_IF_h
#define generatore_IF_h


/*
 *  Generatore con funzione inversa - esponenziale
 */

#include "generatore.h"

class generatore_IF:public generatore {
    public:
        // Costruttore
        generatore_IF(int seed, double min, double max);
        // Distruttore
        ~generatore_IF();

        // Genera un numero casuale
        double generate();

    private:
        // Minimo valore di X su cui generare
        double m_min;
        // Massimo valore di X su cui generare
        double m_max;

        // Genera un numero casuale da distribuzione esponenziale
        double rand_IF(double y);
};

#endif
  • generatore_IF.cc

#include "generatore_IF.h"

#include <iostream>
#include <cmath> // log

using std::cout;
using std::endl;

/*
 *  Costruttore
 */
generatore_IF::generatore_IF(int seed, double min, double max): m_min(min), m_max(max), generatore(seed) {
    cout << "Chiamato il costruttore del generatore con funzione inversa" << endl;
}

/*
 *  Distruttore
 */
generatore_IF::~generatore_IF() {
    cout << "Chiamato il distruttore del generatore con funzione inversa" << endl;
}

/*
 *  Genera un numero casuale
 */
double generatore_IF::generate() {
    double rand_val = rand_range(m_min, m_max);
    return rand_IF(rand_val);
}

/*
 *  Genera un numero casuale da e^(-0.1*x)
 */
double generatore_IF::rand_IF(double y) {
    double lambda = 0.1;
    return - (log(1-y)/lambda);
}
  • generatore_LC.h

#ifndef generatore_LC_h
#define generatore_LC_h

/*
 *  Generatore con teorema del limite centrale
 */

#include "generatore.h"

class generatore_LC: public generatore {
    public:
        // Costruttore
        generatore_LC(int seed, int N, double min, double max);
        // Distruttore
        ~generatore_LC();

        // Genera numeri casuali
        double generate();

    private:
        // Numero di estrazioni 
        int m_N;

        // Valore minimo
        double m_min;
        // Valore massimo
        double m_max;
};

#endif
  • generatore_LC.cc

#include "generatore_LC.h"

#include <iostream>

using std::cout;
using std::endl;

/*
 *  Costruttore
 */
generatore_LC::generatore_LC(int seed, int N, double min, double max): m_N(N), m_min(min), m_max(max), generatore(seed) {
    cout << "Chiamato il costruttore del generatore con teorema del limite centrale" << endl;
}

/*
 *  Distruttore
 */
generatore_LC::~generatore_LC() {
    cout << "Chiamato il distruttore del generatore con teorema del limite centrale" << endl;
}

/*
 *  Genera numeri casuali
 */
double generatore_LC::generate() {
    double mean = 0;

    for(int i = 0; i < m_N; i++) 
        mean += rand_range(m_min, m_max);

    return mean/m_N;
}
  • generatore_TC.h

#ifndef generatoreTC
#define generatoreTC

/*
 *  Generatore Try-And-Catch
 */ 

#include "generatore.h"

enum funzioni {
    kGAUS,
    kEXP
};

class generatore_TC: public generatore {
    public:
        // Costruttore - parametri
        generatore_TC(int seed, unsigned int function = kGAUS, double min = 0., double max = 1.);
        // Distruttore
        ~generatore_TC();

        // Genera un numero casuale dalla distribuzione richiesta
        double generate();

    private:
        // Seleziona il tipo di funzione da utilizzare
        unsigned int m_function;

        // Minimo valore X per l'estrazione
        double m_min;
        // Massimo valore X per l'estrazione
        double m_max;

        // Genera un numero casuale con try and catch
        double rand_TAC(double f(double), double y_max);

        // Calcola il valore della gaussiana (centro=0, sigma=5)
        static double eval_gaus(double x);
        // Calcola il valore dell'esponenziale e^(-0.1*x)
        static double eval_exp(double x);

};

#endif
  • generatore_TC.cc

#include "generatore_TC.h"

#include <iostream>
#include <cmath>

using std::cout;
using std::endl;

/*
 *  Costruttore
 */
generatore_TC::generatore_TC(int seed, unsigned int function, double min, double max) : m_function(function), m_min(min), m_max(max), generatore(seed) {
    cout << "Chiamato il costruttore del generatore try and catch" << endl;

    if( m_function > 1 ) {
        cout << "Funzione non valida!" << endl;
    }
}

/*
 *  Distruttore
 */
generatore_TC::~generatore_TC() {
    cout << "Chiamato il distruttore del generatore try and catch" << endl;
}

/*
 *  Genera un numero casuale dalla distribuzione richiesta
 */
double generatore_TC::generate() {

    double y_max = 0;
    double val = 0;

    double x = 0;
    double y = 0;
        
    switch( m_function ) {
        case kGAUS:
            // Valuto y_max a seconda di m_min e m_max
            if( m_min < 0 && m_max > 0 ) {
                // La gaussiana passa da 0, il massimo è quello della gaussiana
                y_max = eval_gaus(0); 
            } else {
                // La gaussiana non passa da 0, è monotona
                y_max = eval_gaus(m_min);
                double y2 = eval_gaus(m_max);
                if( y2 > y_max )
                    y_max = y2;
            }
            val = rand_TAC(eval_gaus, y_max);

            break;
        case kEXP:
            // Calcolo di y_max - esponenziale monotono decrescente
            y_max = eval_exp(m_min); 

            val = rand_TAC(eval_exp, y_max);

            break;
        default:
            // Solo Gaus e Exp sono implementate
            break;
    }
    
    return val;
}

/*
 *  Genera un numero casuale con try and catch
 */
double generatore_TC::rand_TAC(double f(double), double y_max) {
    double x = 0;
    double y = 0;

    do {
        x = rand_range(m_min, m_max);
        y = rand_range(0, y_max);
    } while( y > f(x) );

    return x;
}

/*
 *  Calcola il valore della gaussiana (centro=0, sigma=5)
 */ 
double generatore_TC::eval_gaus(double x) {
    return 1./sqrt(2*M_PI*5.*5.) * exp( -0.5 * pow( x/5., 2));
}

/*
 *  Calcola il valore dell'esponenziale e^(-0.1*x)
 */
double generatore_TC::eval_exp(double x) {
    return exp(-0.1*x);
}
../_images/linea.png