trasparenti - INFN

Transcript

trasparenti - INFN
Oggetti e Classi (CAP 10)
Alberto Garfagnini, Marco Mazzocco
Università degli studi di Padova
30 Novembre 2011
La programmazione ad oggetti (OOP)
• È un approccio concettuale alla programmazione
(indipendente dal linguaggio utilizzato)
• Il linguaggio C++ aggiunge nuovi elementi rispetto al C che
permettono di implementare con facilità tale approccio.
• Gli aspetti più importanti di OOP sono:
1. Abstrazione
2. Incapsulamento e Data Hiding
3. Polimorfismo
4. Ereditarietà
5. Riutilizzo del Codice
• La Classe è lo strumento più importante del C++ per
implementare la programmazione ad oggetti.
Astrazione e Classi
• È il passo importante per rappresentare l’informazione come
interfaccia con l’utilizzatore:
1. si astraggono le operazioni essenziali di un problema;
2. si esprime la soluzione in quei termini.
Es: Poligoni regolari
Es: Vettore nel piano
• Def. rappresentazione (dati):
1. numero di lati
2. lunghezza di un lato
• Def. rappresentazione (dati):
1. coordinate (x, y)
2. coordinate (ρ,φ)
3. rappr (POLARE/CARTESIANA)
• Def. interfaccia (azioni):
1. calcolare l’area
2. calcolare il perimetro
• Def. interfaccia (azioni):
1. imposta coordinate
2. imposta rappresentazione
3. somma due Vettori
4. sottrai due Vettori
5. moltiplica Vettore per costante
Astrazione e Classi (2)
• La Classe è il veicolo per tradurre una astrazione in un tipo
definito dal programmatore
• Permette di combinare insieme la rappresentazione dei dati e i
metodi per manipolarli
• la scrittura di una classe comporta la specifica di
1. una class declaration, che descrive i dati che compongono
l’oggetto come data members e l’interfaccia pubblica come
member functions, chiamate anche metodi
2. una class method definition, che descrive come sono
implementate le funzioni membri della classe
Class Declaration
Overview della Classe
Methods Definition
Implementazione Dettagliata
Sviluppo di una Classe
• Tipicamente, durante il disegno di una nuova classe (i.e. un
nuovo tipo) si procede per passi successivi e si scrivono
separatamente
1. un file header (es poligono.h)
contiene l’interfaccia tra la classe e l’utilizzatore, sotto forma di
definizione della classe.
si compone di una parte pubblica accessibile dall’utilizzatore e
privata disponibile soltanto ai metodi della classe
2. un file sorgente (es poligono.cxx)
contiene il codice, vero e proprio, che implementa i metodi della
classe.
deve rispettare tutte le definizioni (prototipi) che sono stati definiti
dall’interfaccia
Es. Classe Poligono. File poligono.h
// File: poligono.h
#ifndef POLIGONO_H
#define POLIGONO_H
class Poligono {
private :
int
n_lati;
double lato;
public :
void
double
double
double
};
imposta_valori(double lung_lato, int nlati);
calcola_area();
calcola_perimetro();
stampa();
// Importante il punto-e-virgola
#endif
Commenti alla classe Poligono
• La parola chiave class definisce un nuovo tipo e permette di dichiarare
variabili, chiamate oggetti o istanze della classe Poligono
Poligono quad_1, pent_1;
definiscono due oggetti quad_1 e pent_1 come istanze della classe
Poligono
• Le parole chiave private e public definiscono la modalità di accesso
ai membri della classe (varibili e funzioni).
• Un programma che utilizza un oggetto di una classe può accedere
direttamente soltanto alla sezione pubblica della classe
• Accedere ai membri privati della classe è possibile soltanto tramite
funzioni pubbliche
Per accedere alle variabili n_lati e lato bisogna passare attraverso il
metodo pubblico imposta_valori
• Questo meccanismo di isolamento dei dati della classe prende il nome
di data hiding
Es. Classe Poligono. File poligono.h
parola class identifica la
definizione della classe
parola private identifica
membri della classe ai
quali si può avere accesso solo dalla parte
pubblica (data hiding)
il nome della classe diventa
un nuovo tipo
class Poligono {
i membri della classe possono essere tipi o funzioni
private :
int
n_lati;
double lato;
public :
void
imposta_valori(double llato,
int nlati);
double calcola_area();
double calcola_perimetro();
double stampa();
parola public identifica
membri della classe che
costituiscono l’interfaccia
pubblica della classe
(abstraction)
};
Implementiamo le funzioni, metodi della classe
• Per completare la scrittura delle della classe dobbiamo scrivere il
codice per i metodi specificati nella dichiarazione della classe (file
poligono.h).
• le definizioni dei metodi sono simili alle definizioni delle funzioni
regolari del C++:
• ogni funzione ha un header e un corpo con il codice.
• una funzione può avere argomenti e ritornare un tipo
• ma soprattutto,
• nel definire un metodo di una classe, utilizzare l’operatore :: per
identificare la classe alla quale il metodo appartiene
• i metodi della classe posso accedere alla componente privata della
classe
Es. Classe Poligono. File poligono.cxx (1)
// File: poligono.cxx
#include <iostream>
#include "poligono.h"
void Poligono::imposta_valori(double llato, int nlati)
{
lato = llato;
n_lati = nlati;
}
double Poligono::calcola_perimetro()
{
return n_lati * lato;
}
double Poligono::calcola_area()
{
if (n_lati > 2 and n_lati <6) {
double area = n_lati * lato * lato / 4.;
return area/tan(M_PI/n_lati);
}
}
Es. Classe Poligono. File poligono.cxx (2)
void Poligono::stampa()
{
using namespace std;
switch (n_lati) {
case 3:
cout << "Triangolo" << endl;
break;
case 4:
cout << "Quadrato" << endl;
break;
case 5:
cout << "Pentagono" << endl;
break;
default:
cout << "Poligono sconosciuto. ";
cout << "Numero lati: " << n_lati << endl;
break;
}
return;
}
Usare le classi
• Vogliamo ora scrivere un programma che utilizza la classe definita in
precedenza
• il C++ rende l’utilizzo delle classi, e la creazione di oggetti, il più simile
possibile all’utilizzo dei tipi di base come int e char
• si crea un istanza della classe dichiarando una variabile dei tipo
classe, oppure utilizzando l’operatore new per allocare dinamicamente
un oggetto del tipo classe
• è possibile passare oggetti di una classe a funzioni definite dall’utente,
ritornarli come valori di ritorno e assegnare un oggetto ad un altro
• è persino possibile istruire cin e cout a riconoscere gli oggetti di una
classe e persino fornire una conversione automatica tra oggetti di
classi simili
• vediamo un esempio di utilizzo
// test_poligono.cxx - Prova la classe Poligono
#include <iostream>
#include "poligono.h"
$ g++ -c poligono.cxx
$ g++ test_poligono.cxx poligono.o
int main( )
{
using namespace std;
Poligono tri;
tri.imposta_valori(2.5, 3);
Poligono qua;
qua.imposta_valori(2.5, 4);
./a.out
Poligono: Triangolo
Area : 2.70633 Perimetro : 7.5
Poligono: Quadrato
Area : 6.25 Perimetro : 10
cout << "Poligono: ";
tri.stampa();
cout << "Area : " << tri.calcola_area()
<< " Perimetro : " << tri.calcola_perimetro() << endl;
cout << "Poligono: ";
qua.stampa();
cout << "Area : " << qua.calcola_area()
<< " Perimetro : " << qua.calcola_perimetro() << endl;
return 0;
}
Costruttori e Distruttori di Classi
• Riguardiamo la definizione della classe Poligono
• subito dopo aver istanziato un oggetto della classe con l’istruzione
Poligono tri;
• è necessario invocare un metodo pubblico per inizializzare le variabili
della classe (della sezione private)
tri.imposta_valori(2.5, 3);
• altrimenti le variabili n_lati e lato non sono correttamente
inizializzate ai valori voluti
• inoltre l’unico modo per impostare dei valori è tramite un metodo
dell’interfaccia pubblica (perchè le variabili sono membri della parte
private)
• il C++ fornisce una maniera per inizializzare un oggetto definendo dei
metodi speciali costruttori e distruttori invocati ogni volta che si
istanzia un oggetto di una classe o che l’oggetto venga eliminato dalla
memoria (e quindi non è più disponibile nel programma)
Costruttori di Default
• Tutte le volte che creiamo un oggetto
Poligono tri;
• viene invocato un metodo default constructor
• se il metodo non esiste nell’interfaccia della classe (header file) viene
fornito dal compilatore nella fase di compilazione
• il metodo è vuoto (non contiene codice):
// File: poligono.h
class Poligono {
...
public :
Poligono();
...
};
// File: poligono.cxx
...
Poligono::Poligono()
{
}
...
Costruttori e overloading
• Il default constructor viene invocato tutte le volte che si istanzia un oggetto
senza passare valori di inzializzazione
Poligono tri;
• È possibile definire altri costruttori, tramite l’overloading
// File: poligono.h
class Poligono {
...
public :
Poligono();
Poligono(double llato,
int nlati);
...
};
// File: poligono.cxx
...
Poligono::Poligono(double llato,
int nlati)
{
lato = llato;
n_lati = nlati;
}
Poligono::Poligono()
{
lato = 0.0;
n_lati = 0;
}
Costruttori
• Nel disegnare una classe, scrivere sempre un costruttore di default in
modo da inzializzare in maniera implicita tutti i membri della classe
Poligono tri;
// chiama implicitamente
// il default constructor
Poligono qua = Poligono();
// lo chiama esplicitamente
Poligono * apo = new Poligono; // lo chiama implicitamente
Poligono * penta = new Poligono(12.5, 5);
// chiama un altro
// constructor
• Tutte le volte che si invoca implicitamente il costruttore di default non si
usano le parentesi
Distruttori di Classi
• Quando si crea un oggetto con un costruttore, il programma si
impegna a tenere traccia dell’oggetto fino a quando scade
• in quel momento il programma invoca un metodo speciale chiamato
distruttore che ha lo scopo di liberare eventuale memoria dinamica
allocata dai metodi costruttori.
// File: poligono.h
class Poligono {
...
public :
~Poligono();
...
};
// File: poligono.cxx
...
Poligono::~Poligono()
{
std::cout << "Distruttore";
std::cout << std::endl;
}
• Il distruttore viene invocato automaticamente se un oggetto va
out-of-scope
• non deve essere chiamato direttamente dal programma
Es. Classe Poligono modificata. File poligono2.h
// File: poligono.h
#ifndef POLIGONO_H
#define POLIGONO_H
class Poligono {
private :
int
n_lati;
double lato;
public :
Poligono(double lung_lato, int nlati); // spec constructor
Poligono();
// def constructor
~Poligono();
// destructor
double calcola_area();
double calcola_perimetro();
double stampa();
};
#endif
Es. Classe Poligono. File poligono2.cxx (1)
// File: poligono.cxx
#include <iostream>
#include "poligono2.h"
Poligono::Poligono()
{
cout << "Default constructor called";
lato = 0.0;
n_lati = 0;
}
Poligono::Poligono(double llato, int nlati)
{
cout << "Specialized constructor called";
lato = llato;
n_lati = nlati;
}
Poligono::~Poligono()
{
cout << "Destructor called";
}
$ g++ -c poligono2.cxx
$ g++ test_poligono2.cxx poligono2.o
// test_poligono2.cxx - Prova la classe Poligono
#include <iostream>
./a.out
#include "poligono.h"
Specialized constructor called
Specialized constructor called
int main( )
Default constructor called
{
Poligono: Triangolo
using namespace std;
Area : 2.70633 Perimetro : 7.5
Poligono tri = Poligono(2.5, 3); Poligono: Quadrato
Poligono qua(2.5, 4);
Area : 6.25 Perimetro : 10
Poligono pen;
Destructor called
Destructor called
cout << "Poligono: ";
Destructor called
tri.stampa();
cout << "Area : " << tri.calcola_area()
<< " Perimetro : " << tri.calcola_perimetro() << endl;
cout << "Poligono: ";
qua.stampa();
cout << "Area : " << qua.calcola_area()
<< " Perimetro : " << qua.calcola_perimetro() << endl;
return 0;
}