Il costruttore di copia

Transcript

Il costruttore di copia
Corso di Programmazione ad Oggetti
Costruttori di copia, funzioni di accesso e variabili static
a.a. 2008/2009
Claudio De Stefano
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
1
… Richiami sui Costruttori
■
Un costruttore è una particolare funzione membro di una classe che porta lo
stesso nome della classe.
■
Per le funzioni costruttore non deve essere specificato il tipo restituito, in quanto in
C++ le funzioni costruttore non possono restituire valore.
■
I costruttori possono avere anche uno o più parametri.
Esempio
class Contatore {
public:
Contatore();
.
.
};
Contatore::Contatore()
{
value = 0;
}
Contatore::Contatore(int val)
{
value = val;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
2
… Richiami sui Costruttori
■
Il costruttore di un oggetto viene chiamato automaticamente nel momento in cui
deve essere creato l'oggetto.
Esempi
#include “Contatore.h”
Contatore cont1, cont2(100);
Contatore cont_ptr1, cont_ptr2;
cont_ptr1 = new Contatore;
cont_ptr2 = new Contatore(10);
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
3
… Richiami sui Distruttori
■
Un distruttore è una funzione membro che:
– è necessaria solo se l’oggetto presenta un’estensione dinamica
– ha lo stesso nome della classe, preceduto da ~ (tilde)
– non restituisce risultato (neanche void)
– non ha alcun parametro
■
Generalmente lo scopo dei distruttori è quello di deallocare l’estensione dinamica
di un oggetto
■
NON può essere invocata esplicitamente dal programma utente, ma viene
invocata implicitamente dal compilatore quando viene deallocato lo spazio di
memoria assegnato all'oggetto.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
4
Esempio di distruttore
■
Per quanto detto in precedenza, il costruttore serve per deallocare le estensioni
dinamiche degli oggetti.
Esempio
Class Stack {
public:
Stack();
~Stack();
.
.
// Costruttore
Stack::Stack()
{
st_ptr = new int[SIZE];
num = 0;
}
private:
int *st_ptr;
int num;
.
.
};
// Distruttore
Stack::~Stack()
{
delete [] st_ptr;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
5
Esecuzione dei distruttori
■
I distruttori vengono eseguiti anche quando vengono deallocati
precedentemente allocati dinamicamente.
oggetti
Esempio
void funz()
{
Stack *st_ptr;
.
.
.
delete st_ptr;
Viene invocato
il distruttore di stack
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
6
Il costruttore di copia
■
Crea un oggetto a partire da un altro oggetto della classe (i valori delle variabili
membro vengono copiati).
■
La sintassi dei costruttori di copia è la seguente:
class Myclass{
.
.
Myclass(const Myclass &oggetto);
.
.
};
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
7
Chiamata dei costruttori di copia
I costruttori di copia sono chiamati in maniera implicita:
■
■
■
All’atto della definizione di un oggetto per inizializzarlo con il valore di un altro
oggetto:
Myclass m2=m1; oppure
Myclass m2(m1);
All’atto della chiamata di una funzione per inizializzare un argomento (oggetto)
passato per valore.
All’atto del ritorno da una funzione, per restituire un oggetto per valore
La definzione di un costruttore di copia consente di risolvere tutti i problemi visti in
precedenza relativi alla copia di oggetti che hanno un'estensione.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
8
Confronto tra costruttori di copia
■
In mancanza della definizione del costruttore di copia, il compilatore richiama un
costruttore di copia di default
■
Il costruttore di copia di default esegue una copia copia bit a bit, limitata alla sola
parte base
■
Nel caso esista una estensione dinamica il costruttore di copia deve essere
esplicitamente definito dal progettista della classe
Costrutture di copia di default (bit a bit)
obj
Estensione di obj
Copia di obj
Costrutture di copia di ad hoc
obj
Estensione di obj
Copia di obj
Copia dell'estensione
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
9
Un esempio: la classe stack
■
Poniamo di avere la seguente classe:
Class Stack {
public:
Stack();
Stack(int dim);
~Stack();
Stack(const &Stack)
.
.
.
private:
int *st_ptr;
int num;
int size:
};
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
10
… la classe stack
// Costruttore 1: alloca un array con una dimensione di default
Stack::Stack()
{
st_ptr = new int[STACK_SIZE];
num = 0;
size = STACK_SIZE;
}
// Costruttore 2: alloca un array di dimensione dim
Stack::Stack(int dim)
{
st_ptr = new int[dim];
num = 0;
size = dim;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
11
… la classe stack
// Costruttore di copia
Stack::Stack(const Stack &s)
{
int i;
num = s.num;
dim = s.dim;
s_ptr = new int[dim];
for (i=0; i < num; ++i)
s_ptr[i] = s.s_ptr[i];
}
// Distruttore
Stack::~Stack()
{
delete [] st_ptr;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
12
Variabili static
Le variabili di una classe possono essere dichiarati static.
■
Quando la dichiarazione di una variabile membro è preceduta dalla parola static,
si chiede al compilatore di creare una sola copia di quella variabile per tutti gli
oggetti della classe.
■
Questo significa che tutti gli oggetti di quella classe utilizzeranno la stessa
variabile (per individuarla si usa il nome della classe con l'operatore :: ); in questo
modo è possibile condividere la stessa informazione tra più oggetti della stessa
classe.
■
Quando si dichiarano variabili static non si alloca spazio di memoria all'interno
degli oggetti della classe per tali variabili.
■
L'utilità delle variabili static deriva dal fatto che in questo modo è possibile
condividere la stessa informazione tra più oggetti della stessa classe.
■
Tutte le variabili static vengono inizializzate a zero nel momento in cui viene
creato il primo oggetto.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
13
Variabili static
■
Le variabili static non vengono create tramite istanze della classe a cui
appartengono, ma devono essere definite all’esterno della classe ma nello stesso
ambito in cui è definita la classe. Nei rari casi, però, in cui la classe è definita in un
block scope, le variabili static non sono ammesse. Pertanto una variabile static
può essere definita solo in un namespace (se la classe è definita in quel
namespace) o nel namespace globale.
■
Le variabili static possono essere private o pubbliche; se una variabile di classe
static è privata, tale variabile potrà essere gestita solo da un metodo della
classe a cui appartiene
■
Le variabili static sono molto utili per gestire informazioni comuni a tutti gli oggetti
di una classe (per esempio possono fornire i dati di default per l'inizializzazione
degli oggetti), ma nel contempo, essendo esse stesse membri della classe,
permettono di evitare il ricorso a variabili esterne, salvaguardando così il data
hiding e l'indipendenza del codice di implementazione della classe dalle altre parti
del programma.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
14
Variabili static: esempio 1
■
Le variabili di tipo static possono essere usate per contare il numero di oggetti
istanziati di un certa classe:
class Counter {
public:
static int count;
Counter(){++count;};
~Counter(){--count;};
};
int Counter::Count;
main()
{
Counter count_array[100];
cout<<endl<<”oggetti esistenti: ”<<Counter::count;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
15
Variabili static: esempio 2
class ShareVar {
static int num;
public:
void setnum(int i) { num = i; };
void shownum() { cout << num << " "; }
};
int ShareVar::num; // definisce num
int main()
{
ShareVar a, b;
a.shownum(); // visualizza 0
b.shownum(); // visualizza 0
a.setnum(10); // imposta static num a 10
a.shownum(); // visualizza 10
b.shownum(); // anche questa istruzione visualizza 10
return 0;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
16
Variabili static: esempio 3
■
Le variabili di tipo static possono essere usate per condividere risorse comuni a
tutti gli oggetti di una classe:
class Myclass {
static int resource;
public:
Myclass();
.
.
bool get_resource();
void free_resource(){ resource = 0; };
void shownum() { cout << num << " "; }
};
bool cl::get_resource()
{
if (resource == 0) {
resource = 1;
return true;
} else return false;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
17
Variabili static: esempio 3
main()
{
Myclass m1, m2;
if (m1.get_resource())
cout<<” la risorsa è di m1”;
if (!m2.get_resource())
cout<<” m2 non può utilizzare la risorsa”;
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
18
Funzioni membro di tipo static
■
Anche le funzioni membro possono essere di tipo static.
■
Le funzioni definite static possono accedere solo alle variabili static della classe.
Non hanno inoltre il puntatore this;
■
L'uso di queste funzioni è piuttosto limitato. Vengono di solito usate per
inizializzare le variabili static di una classe.
class A { .....
static int conta( ) ; (prototipo)
..... };
int A::conta( )
} (definizione)
{
.....
Nel prog. chiamante:
int n = A::conta( );
■
Nella chiamata di una funzione-membro static, bisogna qualificare il suo nome
con quello della classe di appartenenza. Si noti che, nella definizione della
funzione, lo specificatore static non va messo (per lo stesso motivo per cui non va
messo davanti alla definizione di un dato-membro static).
■
Se una funzione-membro static deve operare su un oggetto, questo deve essere
trasmesso esplicitamente come argomento.
■
quando un metodo deve operare direttamente su un oggetto (uno e uno solo alla
volta), è più conveniente che sia incapsulato nell'oggetto stesso e quindi non
venga dichiarato static.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
19
Funzioni di accesso alle variabili membro: lettura
■
Sappiamo che lo stato di un oggetto è determinato dal valore delle variabili da
esso contenuto.
■
È buona norma della programmazione OO definire tali variabili private
■
Per consentire agli utenti di una classe di visualizzare lo stato di un oggetto
istanziato è necessario quindi definire delle funzioni che consentono di accedere
alle sue variabili.
■
Una tipica definizione di funzione di accesso è del tipo:
Tipo get_value(){return value;}
■
Tipicamente queste funzioni sono definite all'interno della dichiarazione della
classe.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
20
Funzioni di accesso alle variabili membro: modifica
■
Oltre che leggere lo stato di un oggetto è anche necessario modificarlo.
■
Pertanto bisogna definire anche delle funzioni che consentono la modifica delle
variabili di un oggetto.
■
Una tipica definizione di funzione di modifica è del tipo:
void set_value(Tipo val){var = val;}
NOTA
La scelta di nomi del tipo get_nomevar e set_nomevar è una buona norma di
programmazione, ma non è assolutamente prescritta dal linguaggio C++
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
21
Esempio
Class Myclass {
public:
// Funzioni Costruttore
Myclass();
.
.
// Funzioni di accesso
int get_N(){return N;}; //restituisce il valore di N
char get_ch(){return ch;}; //restituisce il valore di ch
void set_N(int val){N = val;}; // assegna valore a N
void set_ch(char c){char = c;}; // assegna valore a ch
private:
int N;
char ch;
float x;
};
Le funzioni membro definite nella dichiarazione
de vengono automaticamente trasformate
in funzioni inline dal compilatore
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
22
L'accesso ai membri di una classe
■
Quando definiamo una funzione membro di una classe, possiamo fare riferimento
alle variabili membro della classe senza ambiguità.
Class Myclass {
public:
Myclass();
.
.
funz(Tipo val);
.
.
private:
Tipo1 var1;
Tipo1 var2;
Myclass::funz(Tipo val)
{
var2 = pow(x, 2);
}
main()
{
Myclass m1, m2;
m1.funz(); // modifica le variabili di m1
m2.funz(); // modifica le variabili di m2
};
}
Domanda
ma qual'è il meccanismo che consente alla funzione membro di individuare le variabili specifiche
dell'oggetto sul quale è stata chiamata?
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
23
Meccanismo di accesso ai membri di una classe
■
Nella chiamata di una funzione membro il compilatore introduce un primo
parametro nascosto che è l’indirizzo dell’oggetto proprio a cui f viene applicata.
■
Tale parametro e’ un puntatore costante il cui nome è this.
Myclass.h modificato
Myclass.h
Class Myclass {
.
.
Tipo funz(Tipo val);
.
.
};
Viene trasformata
dal compilatore in
Class Myclass {
.
.
Tipo funz(Myclass* const this, Tipo val);
.
.
};
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
24
Il puntatore This
■
Il ruolo del puntatore this è quello di identificare l'oggetto istanziato al quale
applicare una certa funzione della classe.
■
Infatti quando si definisce una funzione membro non si specifica in alcun modo
l'oggetto sul quale la funzione verrà chiamata.
■
Questa operazione viene fatta in maniera automatica dal compilatore. Il quale,
modifica i riferimenti alle variabili di classe aggiungendo il puntatore this.
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
25
Esempio
■
Ovviamente la traformazione interessa anche il file .cpp:
Myclass.cpp
Tipo Myclass::funz(Tipo1 val)
{
var1 = pow(2, val);
};
Compilatore
Myclass.cpp modificato
Tipo Myclass::funz(Myclass* const this, Tipo1 val)
{
this->var1 = pow(2, val);
};
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
26
Chiamata delle funzioni membro
■
L'istruzione di chiamata di una funzione membro su una specifica istanza di una
classe viene anch'essa trasformata dal compilatore.
■
In pratica l'indirizzo dell'oggetto (tramite l'operatore &) viene ricopiato in this e
attraverso tale puntatore la funzione opera sull’oggetto proprio:
main ()
{
Myclass m, *mp;
Tipo1 x;
.
.
m.funz(x);
.
.
mp->funz(x);
}
compilatore
main ()
{
Myclass m;
Tipo1 x;
.
.
funz(&m, x);
.
.
funz(mp, x);
}
Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009
27