• Non ci sono risultati.

Corso di Programmazione ad Oggetti

N/A
N/A
Protected

Academic year: 2021

Condividi "Corso di Programmazione ad Oggetti"

Copied!
27
0
0

Testo completo

(1)

Corso di Programmazione ad Oggetti

Costruttori di copia, funzioni di accesso e variabili static

a.a. 2008/2009

Claudio De Stefano

(2)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

2

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

}

(3)

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

(4)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

4

… 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.

(5)

Esempio di distruttore

■ Per quanto detto in precedenza, il costruttore serve per deallocare le estensioni dinamiche degli oggetti.

Esempio

Class Stack { public:

Stack();

~Stack();

. . private:

int *st_ptr;

int num;

. . };

// Costruttore Stack::Stack() {

st_ptr = new int[SIZE];

num = 0;

}

// Distruttore Stack::~Stack() {

delete [] st_ptr;

}

(6)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

6

Esecuzione dei distruttori

■ I distruttori vengono eseguiti anche quando vengono deallocati oggetti precedentemente allocati dinamicamente.

Esempio

void funz() {

Stack *st_ptr;

. . .

delete st_ptr;

}

Viene invocato

il distruttore di stack

(7)

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

. . };

(8)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

8

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.

(9)

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

obj

Copia di obj

Estensione di obj

Costrutture di copia di default (bit a bit) Costrutture di copia di ad hoc

obj

Copia di obj

Estensione di obj

Copia dell'estensione

(10)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

10

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:

};

(11)

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

}

(12)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

12

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

}

(13)

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.

(14)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

14

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.

(15)

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;

(16)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

16

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;

}

(17)

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;

(18)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

18

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

}

(19)

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.

■ 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

intn = A::conta( );

... };

Nel prog. chiamante:

static int conta( ) ; (prototipo)

int A::conta( ) { ...

} (definizione) class A { ...

(20)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

20

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.

(21)

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++

(22)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

22

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

(23)

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?

(24)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

24

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.

Class Myclass { .

.

Tipo funz(Tipo val);

. . };

Viene trasformata dal compilatore in

Class Myclass { .

.

Tipo funz(Myclass* const this, Tipo val);

. . };

Myclass.h modificato Myclass.h

(25)

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.

(26)

Claudio De Stefano - Corso di Programmazione ad Oggetti - a.a. 2008/2009

26

Esempio

■ Ovviamente la traformazione interessa anche il file .cpp:

Tipo Myclass::funz(Myclass* const this, Tipo1 val) {

this->var1 = pow(2, val);

};

Tipo Myclass::funz(Tipo1 val) {

var1 = pow(2, val);

};

Myclass.cpp

Compilatore

Myclass.cpp modificato

(27)

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

}

main () {

Myclass m;

Tipo1 x;

. .

funz(&m, x);

. .

funz(mp, x);

} compilatore

Riferimenti

Documenti correlati

quando viene istanziato da un altro oggetto della stessa classe. l’argomento è una reference ad un

 definisce le variabili (memoria privata) ed i metodi (interfaccia) di tali oggetti.  altri punti

Dipartimento di Informatica Università “La Sapienza”.

public void draw(Window w, Position where){…}. public

 Un errore in esecuzione, perch`e il metodo propone restituisce un oggetto di tipo dinamico LeggeTaglio;.  un errore in compilazione perch`e il metodo propone non `e

 Niente, perch´e dar`a un errore a tempo di compilazione Domanda 23 L’istruzione p.canta(g); stamper` a:. 

■ La funzione costruttore di un oggetto locale (definito all'interno di una funzione o di un blocco di istruzioni) viene eseguita nel momento in cui viene incontrata l'istruzione

■ così come una classe è un modello per istanziare oggetti (della classe) a tempo d'esecuzione, un template è un modello per istanziare classi o funzioni (del template) a tempo