Soluzioni: Appendice 2
Contents
Soluzioni: Appendice 2¶

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;
}

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);
}
