Laboratorio di Algoritmi e Strutture Dati Esercizi - LACAM

Transcript

Laboratorio di Algoritmi e Strutture Dati Esercizi - LACAM
Laboratorio di
Algoritmi e Strutture Dati
Esercizi
Teresa M.A. Basile
– [email protected] –
Dipartimento di Informatica
Università degli Studi di Bari “Aldo Moro”
1
Operatori Indispensabili
●
Ogni Classe che utilizza della memoria allocata dinamicamente
dovrebbe solitamente essere provvista del gruppo di funzioni
membro costituito da:
●
●
●
●
Costruttore:
Distruttore
Costruttore di copia
Operatore ridefinito di assegnamento (overloading operator=)
Ogni classe che possiede un distruttore che esegue compiti non banali, quali
deallocare memoria dinamica, dovrebbe essere completamente corredata di
tutte le funzioni che controllano la costruzione, la distruzione e la copia
2
Costruttore
●
●
●
●
●
Utilizzato automaticamente dal compilatore quando una istanza
della classe viene creata, in modo che tale istanza sia sin dall'inizio
in uno stato consistente.
Un costruttore altro non è che un metodo il cui nome è lo stesso di
quello della classe.
Un costruttore può avere un qualsiasi numero di parametri, ma
non restituisce mai alcun tipo (neanche void); il suo scopo è quello
di inizializzare le istanze della classe
Una classe può possedere più costruttori, cioè i costruttori possono
essere overloaded, in modo da offrire diversi modi per inizializzare
una istanza
Il costruttore di default è particolare, in quanto è quello che il
compilatore chiama quando il programmatore non utilizza
esplicitamente un costruttore nella dichiarazione di un oggetto
3
Costruttore di copia
●
●
●
●
●
Le funzioni possono ricevere un oggetto come argomento e possono
restituirne uno.
Per default entrambe le operazioni sono effettuate per valore, cioè la
funzione riceve o restituisce una copia dell'oggetto
In tali casi il C++ crea una copia dell'oggetto da passare e chiama il
costruttore di copia per copiare I valori dell'oggetto originale nell'oggetto
copia
In generale il costruttore di copia di una classe D viene invocato
●
in una inizializzazione di variabile (Matrice M1=M2) ;
●
nel passaggio di parametri per valore
●
per i valori di ritorno
●
per la gestione di eccezioni
Per ogni classe il compilatore crea un costruttore di copia di default, che
copia ogni dat membro dell'oggetto originale nel corrispondente dato
membro del nuovo oggetto.
Il costruttore di copia fornito dal compilatore esegue una
copia degli attributi. In generale questo è sufficiente, ma
quando una classe contiene puntatori è necessario
definirlo esplicitamente onde evitare problemi di
condivisione di aree di memoria
4
Operatore di assegnamento
●
●
Viene invocato in tutte le operazioni che assegnano
all'oggetto dichiarato un altro oggetto
Come nel caso caso del costruttore di copia anche in questo
caso il compilatore fornisce automaticamente un operatore di
assegnamento
5
inizializzazione != assegnamento
operazioni sintatticamente simili
operazioni semanticamente profondamente diverse
●
●
●
●
l'inizializzazione viene compiuta una volta sola, quando l'oggetto viene
creato
un assegnamento invece si esegue su un oggetto precedentemente creato
Il costruttore di copia viene utilizzato quando si dichiara un nuovo oggetto e
si inizializza il suo valore con quello di un altro
l'operatore di assegnamento viene invocato successivamente in tutte le
operazioni che assegnano all'oggetto dichiarato un altro oggetto
6
Overloading Operatori
< ReturnType > operator@( < ArgumentList > ) { < Body > }
●
●
Operatori unari
class String {
public:
bool operator!() const;
...
};
Operatori binari
class String {
public:
const String &operator+=(const String & );
...
};
●
y +=z è equivalente a y.operator+=(z)
7
Distruttore
●
●
●
●
●
●
Poichè ogni oggetto ha una propria durata (lifetime) è necessario disporre
anche di un metodo che permetta una corretta distruzione dell'oggetto stesso,
un distruttore.
Un distruttore è un metodo che non riceve parametri, non ritorna alcun tipo
(neanche void) ed ha lo stesso nome della classe preceduto da una ~ (tilde)
Invocato automaticamente alla fine del blocco applicativo (le istruzioni
racchiuse tra { } ) in cui la variabile è stata dichiarata (alla fine del programma
per variabili globali e statiche).
Il compito del distruttore è quello di assicurarsi della corretta deallocazione
delle risorse e se non ne viene esplicitamente definito uno, il compilatore
genera per ogni classe un distruttore di default che chiama alla fine della
lifetime di una variabile
Poichè il distruttore fornito dal compilatore non tiene conto di aree di memoria
allocate tramite membri puntatore, è sempre necessario definirlo
esplicitamente ogni qual volta esistono membri puntatori. In tutti gli altri casi,
spesso il distruttore di default è più che sufficiente e non occorre scriverlo.
Non è possibile eseguire l'overloading dei distruttori: ogni classe cioè possiede
sempre e solo un unico distruttore.
8
Revisione Classe Matrice
9
Classe Matrice: allocazione statica vs dinamica
// const int DIMMAX = 5;
const double ELEMDEFAULT=0.0;
typedef double tipoelem;
typedef int indice;
class Matrice {
public:
// Costruttore Distruttore
Matrice(indice, indice);
~Matrice();
indice getnrighe();
indice getncolonne();
tipoelem leggimatrice(indice,indice);
void scrivimatrice(indice,indice,tipoelem);
Matrice somma(Matrice);
Matrice prodotto(Matrice);
private:
indice righe;
indice colonne;
// dimensione massima costante - allocazione massima con stesso numero di righe e colonne
// tipoelem elem[DIMMAX][DIMMAX];
tipoelem **elem;
bool controllaindici(indice,indice); // controllo sugli indici
bool controllalimite(indice);// controllo sulla dimensione
void tipomatrice(); // verifica tipo di matrice (interi o reali)
};
10
Classe Matrice: allocazione statica vs dinamica
COSTRUTTORE
// Costruttore – caso A_S
Matrice::Matrice(indice nrighe, indice ncolonne)
{
if (controllalimite(nrighe) && controllalimite(ncolonne)) {
righe=nrighe;
colonne=ncolonne;
// inizializzazione degli elementi
for (indice i=0; i<righe;i++)
for (indice j=0; j<colonne;j++)
{
elem[i][j]=ELEMDEFAULT;
}
}
else
cout << "Almeno uno degli indici fuori limite" << endl;
}
11
Classe Matrice: allocazione statica vs dinamica
COSTRUTTORE
// Costruttore – caso A_D
Matrice::Matrice(indice nrighe, indice ncolonne)
{
righe=nrighe;
colonne=ncolonne;
// allocazione dinamica della matrice
elementi = new tipoelem* [righe];
for (indice i=0; i<righe;i++)
elementi[i] = new tipoelem[colonne];
// inizializzazione degli elementi
for (indice i=0; i<righe;i++)
for (indice j=0; j<colonne;j++)
{
elem[i][j]=ELEMDEFAULT;
}
}
12
Classe Matrice: allocazione statica vs dinamica
DISTRUTTORE
// Distruttore – caso A_S
Matrice::~Matrice()
{}
// Distruttore – caso A_D
Matrice::~Matrice(){
for (indice i=0; i<righe;i++){
// dealloco la memoria per gli array
delete(elem[i]);
}
// dealloco la memoria per il puntatore a puntatori
delete(elem);
}
13
Classe Matrice: allocazione statica vs dinamica
COSTRUTTORE DI COPIA
(.h)
Matrice(const Matrice &);
(.cpp)
// Costruttore di copia – caso A_D
Matrice::Matrice(const Matrice & origin)
{
righe = origin.getnrighe();
colonne = origin.getncolonne();
// allocazione dinamica della matrice
elem = new tipoelem* [righe];
for (indice i=0; i<righe;i++)
elem[i] = new tipoelem[colonne];
// inizializzazione degli elementi
for (indice i=0; i<righe;i++)
for (indice j=0; j<colonne;j++)
{
this->elem[i][j]=origin.elem[i][j];
}
}
14
Classe Matrice: allocazione statica vs dinamica
Operatore di Assegnamento
(.h)
Matrice & operator=(const Matrice &);
(.cpp)
// Operatore di Assegnamento – caso A_D
Matrice & Matrice::operator=(const Matrice & B){
righe = B.getnrighe();
colonne = B.getncolonne();
elem = new tipoelem*[righe];
for(int i=0;i<righe;i++) {
elem[i] = new tipoelem[colonne];
}
}
for(int i = 0; i<B.getnrighe(); i++) {
for (int j = 0; j < B.getncolonne(); j++) {
elem[i][j] = B.elem[i][j];
}
}
return *this;
15
Classe Matrice: overloading operatori
const int DIMMAX = 5;
const double ELEMDEFAULT=0.0;
typedef int indice;
class Matrice {
public:
// Costruttore Distruttore
Matrice(indice, indice);
~Matrice();
indice getnrighe();
indice getncolonne();
tipoelem leggimatrice(indice,indice);
void scrivimatrice(indice,indice,tipoelem);
Matrice somma(Matrice); // operator+
Matrice prodotto(Matrice); //operator*
private:
indice righe;
indice colonne;
tipoelem elem[DIMMAX][DIMMAX];
bool controllaindici(indice,indice); // controllo sugli indici
bool controllalimite(indice);// controllo sulla dimensione
void tipomatrice(); // verifica tipo di matrice (interi o reali)
};
16
Classe Matrice: overloading operatori
(.h)
Matrice operator+(const Matrice & B) const;
(.cpp)
Matrice Matrice::operator+(const Matrice & matrix)
//Matrice Matrice::somma(Matrice matrix) const
{
if (righe==matrix.getnrighe() &&
colonne==matrix.getncolonne())
{
Matrice risultato(righe,colonne);
for (indice i=1; i<=righe;i++)
for (indice j=1; j<=colonne;j++)
{
risultato.scrivimatrice(i,j,leggimatrice(i,j)
+matrix.leggimatrice(i,j));
}
return risultato;
}
else cout << "Somma non possibile!" << endl;
}
17