1 laboratorio di calcolo II AA 2003/04 seconda settimana a cura di Domizia Orestano Dipartimento di Fisica Stanza tel. ( ) UNIVERSITA DEGLI STUDI ROMA TRE DIPARTIMENTO DI FISICA E. AMALDI
2 Indice Puntatori Puntatori e vettori Vettori di caratteri e stringhe Allocazione dinamica della memoria Strutture dati Reference Passaggio dei dati ad una funzione per referenza e per valore nella prossima lezione
3 Puntatori Un puntatore è un oggetto che fa riferimento ad un altro oggetto Lo si dichiara in uno dei due modi seguenti int* p; Dove p è il puntatore Non gli si può assegnare un valore direttamente, ma solo un oggetto cui puntare int i=3;// loggetto salvato in memoria int *p=&i; // dove &i è lindirizzo di memoria di i (questa istruzione equivale a int *p; p=&i;) Loggetto puntato si ottiene dereferenziando il puntatore: *p Se p vale 0 (puntatore nullo) *p non è definito (il programma si ferma!) int * p: i: int 3
4 Esempio int numero = 82485; // un oggetto intero a 32 bit // che in binario si scrive // // int * puntatore; // puntatore ad un oggetto intero puntatore = № // puntatore a numero // (posto uguale all'indirizzo di numero) puntatorenumero 0x x
5 Puntatori e Vettori (Arrays) float x[5]; // dichiarazione di un vettore di numeri reali con 5 elementi x e &x[0] sono la stessa cosa: il puntatore al primo elemento dellarray *x e x[0] sono la stessa cosa: il primo elemento dellarray float * x: x[4]x[0]x[1]x[2] float x[3]
6 Aritmetica dei puntatori float x[5]; float *y=&x[0]; // y è un puntatore a x[0] float *z=x; // z è un puntatore a x[0] (y+1) punta a x[1] *(y+1), y[1] e x[1] accedono lo stesso oggetto sono consentite operazioni di somma e sottrazione ed è possibile confrontare tra loro due puntatori (si confrontano gli indirizzi di memoria, che sono numeri interi)
7 Esempi di uso dei puntatori somma degli elementi di un vettore float x[5]; // qui gli elementi vengono inizializzati… double sum=0; for(int i=0;i<5;i++) { sum+=x[i]; } oppure float x[5]; // qui gli elementi vengono inizializzati… float *y=x; double sum=0; for(int i=0;i<5;i++) { sum+=*y++; }
8 Esempi inversione dellordine degli elementi di un vettore float x[10]; // qui gli elementi vengono inizializzati… float *left = &x[0]; float *right = &x[9]; while(left < right) { float temp = *left; *left++ = *right; *right-- = temp; } azzeramento degli elementi di un vettore float x[10]; // qui gli elementi vengono inizializzati… float *p = &x[10]; //attenzione a non usare *p !!! while ( p != x ) *--p=0;
9 vettori di caratteri e stringhe Programma che usa char e stringhe C #include int main() { // Voglio scrivere il mio nome // Introduco un vettore di caratteri nomechar, // lungo 7 caratteri, ed introduco il mio // nome, 1 lettera alla volta. // L'output utilizza un loop, con o senza il // puntatore (piu' o meno raffinato) // input dei dati char nomechar[7]; nomechar[0] = D'; nomechar[3] = nomechar[5] = i'; nomechar[1] = o'; nomechar[2] = m'; nomechar[4] = z'; nomechar[6] = a';
10 // In realta' sarebbe anche possibile // istanziare e inizializzare nomechar con // una sola istruzione. La dimensione viene // assegnata automaticamente dal compilatore. // La linea di istruzione equivalente alle // precedenti e': // char nomechar[] // = {D,o,m,i,z,i,a }; // Oppure l'input potrebbe essere gestito // tramite lettura da tastiera // cout << "Assegnare il nome( 7 caratteri): " // << endl; // for (int i=0; i<7; i++) { // cin >> nomechar[i]; // };
11 // Passiamo all'output // Senza l'uso del puntatore cout << endl << " Output senza l'uso del puntatore: "; for(int m=0;m<=7;m++) { cout << nomechar[m]; }; //Ora passiamo all'uso delle stringhe di tipo C // Istanzio un puntatore alla stringa C che // contiene il nome char *nomeC; nomeC = Domizia"; // Notare l'uso e la funzione del doppio apice cout << endl << " Stringa di tipo C: " << nomeC << endl;
12 // Da notare che si puo' effettuare l'output // anche pensando alla stringa C come una serie // di caratteri cout << "Output carattere per carattere: "; for(int j=0;j<7;j++) { cout << ' ' << nomeC[j] ; }; cout << endl; // oppure... cout << " "; char *q=nomeC; // q e un puntatore ad un carattere for(int j=0;j<7;j++) { cout << ' ' << *q++; }; cout << endl << endl; return 0; }
13 Programma che usa le stringhe C++ #include int main() { string name; name = Domizia"; cout << name << endl; string cognome=Orestano"; string tutto=name+" "+cognome; cout << endl << tutto << endl; return 0; }
14 Nota al Programma che usa i caratteri e le stringhe C Sarebbe meglio introdurre una variabile cmax const int cmax=7; E poi sostituire tutte le ricorrenze di 7 con la variabile cmax. In questo modo il programma diventa più elegante ed eventuali variazioni di lunghezza non richiederebbero sostituzioni se non nel valore di cmax. Il problema non si pone nell'uso di stringhe C++ (programmazione OO ed allocazione dinamica della memoria)
15 Dimensionamento dei vettori (1) // Un esempio da CorpoCeleste class CorpoCeleste { protected: char *Nome; …………….}; // Variante a dimensione fissa, // memoria allocata a compilation time class CorpoCeleste { protected: char Nome[20] ; ……………};
16 // Tentativo di variante a dimensione variabile, // ma non compila! class CorpoCeleste { protected: int n; char Nome[n]; …………….}; vettori di dimensione variabile, fissata in fase di esecuzione, non possono essere inseriti tra gli attributi di una classe !
17 Puntatori a variabili locali (1) ===File prova_fp.h class prova_fp { private: // public : prova_fp() { } ; ~prova_fp() { } int * int_fp() { int a = 5 ; return &a ; }; ===Main: #include #include "prova_fp.h" int main() { int * p; prova_fp myprova; p = myprova.int_fp(); cout << endl << endl; cout << " Puntatore : " << p << endl ; cout << " Valore : " << *p << endl ; return 0; }; Warning in compilazione e stampa di un Valore privo di senso !
18 Allocazione dinamica della memoria Lallocazione statica degli oggetti in memoria consente maggiore velocità di esecuzione perchè lo spazio usato e il tempo di utilizzo (lifetime delloggetto) sono determinati già in fase di compilazione. due problemi: –vogliamo poter allocare di vettori di dimensione definita durante lesecuzione del programma –vogliamo avere accesso agli oggetti definiti localmente (allinterno di uno { scope}) una soluzione: allocazione dinamica della memoria. Gli oggetti vengono creati in unarea di memoria chiamata heap. Si utilizzano gli operatori –new –delete
19 Dimensionamento dei vettori (2) class CorpoCeleste { protected: char * Nome; …………….}; E poi nel costruttore inizializzato int n = strlen(nomeCorpo); Nome=new char[n]; Ma tutti gli oggetti creati con new devono essere distrutti esplicitamente con loperatore delete, questi oggetti infatti non vengono cancellati automaticamente alluscita dallo scope. In caso di vettori la sintassi è delete [ ] Nome; Dobbiamo modificare CorpoCeleste::~CorpoCeleste inserendovi questa istruzione!
20 #include "CorpoCeleste.h" #include CorpoCeleste::CorpoCeleste() { } CorpoCeleste::CorpoCeleste (const char *nomeCorpo, float mass, float xpos, float ypos, float vxi, float vyi) { Nome = new char[strlen(nomeCorpo)]; strcpy(Nome, nomeCorpo); m = mass; x = xpos; y = ypos; vx = vxi; vy = vyi; } void CorpoCeleste::calcolaPosizione( float fx, float fy, float t) { double ax = fx/m; double ay = fy/m; vx += ax*t; vy += ay*t; x += vx*t; y += vy*t; } CorpoCeleste.cc Prima parte
21 void CorpoCeleste::stampaPosizione() { cout.setf(ios::fixed); cout.setf(ios::showpos); cout << " " << setprecision(4) << setw(9) << x*1.e-11 << " " << setprecision(4) << setw(9) << y*1e-11 ; } void CorpoCeleste::stampaVelocita() { cout.setf(ios::fixed); cout.setf(ios::showpos); cout << " " << vx << " " << vy ; } CorpoCeleste::~CorpoCeleste() {delete [] Nome;} const char* CorpoCeleste::nome() {return Nome; } double CorpoCeleste::M() { return m; } double CorpoCeleste::X() { return x; } double CorpoCeleste::Y() {return y; } double CorpoCeleste::Vx() {return vx; } double CorpoCeleste::Vy() {return vy; } CorpoCeleste.cc Seconda parte
22 Puntatori a variabili locali (2) class prova_fp_n { private: // public : prova_fp_n() { } ; ~prova_fp_n() { } ; int * int_fp() { int * p = new int(5); return p ; }; ===Main: #include #include "prova_fp_n.h" int main() { int * p; prova_fp_n myprova; p = myprova.int_fp(); cout << endl << endl; cout << " Puntatore : " << p << endl ; cout << " Valore : " << *p << endl ; delete p; return 0; }; Nessun warning in compilazione e stampa del Valore corretto: 5!
23 Esercitazione Obiettivi: –Utilizzare la classe CorpoCeleste –Studiare il comportamento dei costruttori e del distruttore –Utilizzare puntatori, vettori, vettori di puntatori 1.Modificare il distruttore di CorpoCeleste includendo loperazione di delete [ ] Nome 2.Aggiungere stampe nei costruttori e nel distruttore 3.Provare i seguenti programmi che usano CorpoCeleste:
24 Programmi Es1_a.cc (costruttore, uso dei metodi Get, distruttore) Es1_b.cc (allocazione dinamica, puntatori) Es1_c.cc (vettore di oggetti, costruttore di default) Es1_cbis.cc (vettore di puntatori ad oggetti) Es1_d.cc (vettore con allocazione dinamica) Es1_dbis.cc (vettore di puntatori con allocazione dinamica)
25 #include "CorpoCeleste.h" #include CorpoCeleste::CorpoCeleste() { cout << invocato costruttore di default di CorpoCeleste<<endl; } CorpoCeleste::CorpoCeleste (const char *nomeCorpo, float mass, float xpos, float ypos, float vxi, float vyi) { Nome = new char[strlen(nomeCorpo)]; strcpy(Nome, nomeCorpo); m = mass; x = xpos; y = ypos; vx = vxi; vy = vyi; cout << invocato costruttore di CorpoCeleste <<Nome<<endl; } void CorpoCeleste::calcolaPosizione( float fx, float fy, float t) { double ax = fx/m; double ay = fy/m; vx += ax*t; vy += ay*t; x += vx*t; y += vy*t; } CorpoCeleste.cc Prima parte
26 void CorpoCeleste::stampaPosizione() { cout.setf(ios::fixed); cout.setf(ios::showpos); cout << " " << setprecision(4) << setw(9) << x*1.e-11 << " " << setprecision(4) << setw(9) << y*1e-11 ; } void CorpoCeleste::stampaVelocita() { cout.setf(ios::fixed); cout.setf(ios::showpos); cout << " " << vx << " " << vy ; } CorpoCeleste::~CorpoCeleste() { cout << invocato distruttore di CorpoCeleste <<Nome<<endl; delete [] Nome; } const char* CorpoCeleste::nome() {return Nome; } double CorpoCeleste::M() { return m; } double CorpoCeleste::X() { return x; } double CorpoCeleste::Y() {return y; } double CorpoCeleste::Vx() {return vx; } double CorpoCeleste::Vy() {return vy; } CorpoCeleste.cc Seconda parte
27 #include #include "CorpoCeleste.h int main () { // istanzio una serie di oggetti di tipo CorpoCeleste CorpoCeleste sole("Sole", 2.e30,0.,0.,0.,0.); CorpoCeleste pietra("dolomite",1.,0.,0.,1.,1.); CorpoCeleste sasso("quarzo",2.,1.,0.,0.,1.); // Scrivo alcune caratteristiche degli oggetti che ho istanziato: // massa e posizione cout << endl << "Pianeta n. Massa Posizione iniziale " << endl << " x y " << endl << endl; cout << " " << "1" << " " << sole.M() << " " << sole.X() << " " << sole.Y() << endl; cout << " " << "2" << " " << pietra.M() << " " << pietra.X() << " " << pietra.Y() << endl; cout << " " << "3" << " " << sasso.M() << " " << sasso.X() << " " << sasso.Y() << endl; cout << endl; // Il programma e' finito. // Il compilatore provvede a distruggere gli oggetti di tipo CorpoCeleste return 0; } Es1_a.cc
28 #include #include "CorpoCeleste.h" int main () { // istanzio una serie di oggetti di tipo CorpoCeleste // Uso l'allocazione dinamica della memoria, tramite new CorpoCeleste * Psole = new CorpoCeleste("Sole", 2.e30,0.,0.,0.,0.); CorpoCeleste * Ppietra = new CorpoCeleste("dolomite",1.,0.,0.,1.,1.); CorpoCeleste * Psasso = new CorpoCeleste("quarzo",2.,1.,0.,0.,1.); // Scrivo alcune caratteristiche degli oggetti che ho istanziato; // Ad esempio la massa e la posizione cout << endl << "Pianeta n. Massa Posizione iniziale " << endl << " x y " << endl << endl; cout M() X() Y() << endl; cout M() X() Y() << endl; cout M() X() Y() << endl; cout << endl; Es1_b.cc Prima parte
29 // Ora devo cancellare gli oggetti allocati dinamicamente tramite new cout << endl << "Prima delle chiamate a delete " << endl; delete Psole; delete Ppietra; delete Psasso; cout << endl << "Dopo le chiamate a delete " << endl; // Il programma e' finito // Notate (tramite il distruttore intelligente) che il compilatore // ha provveduto a distruggere gli oggetti di tipo CorpoCeleste // ai quali puntavano Psole, Ppietra, Psasso. return 0; } Es1_b.cc Seconda parte
30 #include #include "CorpoCeleste.h" int main () { // uso un vettore di oggetti di tipo Corpoceleste const int np = 6; CorpoCeleste Pianeti[np]; // Con un loop scrivo gli elementi del vettore for (int i=0; i<np; i++) { CorpoCeleste Ausiliario(" ",i,i,i,i,i); Pianeti[i] = Ausiliario; } // Con un loop posso estrarre le informazioni cout << endl << "Pianeta n. Massa Posizione iniziale " << endl << " x y " << endl << endl; for ( int i=0; i<np; i++) { cout << " " << i+1 << " " << Pianeti[i].M() << " " << Pianeti[i].X() << " " << Pianeti[i].Y() << endl; } cout << endl; return 0; } Es1_c.cc
31 #include #include "CorpoCeleste.h" int main () { // Ora uso un vettore di puntatori a oggetti di // tipo CorpoCeleste const int npp = 8; CorpoCeleste* MieiPianeti[npp]; // Con un loop scrivo gli elementi del vettore for ( int i=0; i<npp; i++) { MieiPianeti[i] = new CorpoCeleste(" ",i,i,i,i,i); } // Con un loop posso estrarre le informazioni. Ad esempio la massa cout << endl << "Pianeta n. Massa Posizione iniziale " << endl << " x y " << endl << endl; for ( int i=0; i<npp; i++) { cout M() X() << " " Y() << endl; } cout << endl; Es1_cbis.cc Prima parte
32 // devo ricordarmi di cancellare gli oggetti allocati dinamicamente for ( int i=0; i<npp; i++) { cout << i+1; delete MieiPianeti[i]; } return 0; } Es1_cbis.cc Seconda parte
33 #include #include "CorpoCeleste.h" int main () { // uso un vettore di oggetti di tipo Corpoceleste // Il vettore di oggetti e' allocato dinamicamente // tramite new, che fornisce il puntatore all'array const int np = 6; CorpoCeleste * Pianeti= new CorpoCeleste[np]; // Con un loop scrivo gli elementi del vettore for (int i=0; i<np; i++) { CorpoCeleste Ausiliario("",i,i,i,i,i); Pianeti[i] = Ausiliario; } // Con un loop posso estrarre le informazioni. Ad esempio la massa cout << endl << "Pianeta n. Massa Posizione iniziale " << endl << " x y " << endl << endl; for ( int i=0; i<np; i++) { cout << " " << (i+1) << " " << Pianeti[i].M() << " " << Pianeti[i].X() << " " << Pianeti[i].Y() << endl; } Es1_d.cc Prima parte
34 // Ora devo cancellare il vettore allocato dinamicamente cout << endl << endl << " Prima di chiamare delete [] Pianeti " << endl; delete [] Pianeti; cout << endl << " Dopo aver chiamato delete [] Pianeti " << endl; return 0; } Es1_d.cc Seconda parte
35 #include #include "CorpoCeleste.h" int main () { // Ora uso un vettore di puntatori a oggetti di tipo CorpoCeleste // Il vettore di puntatori e' a sua volta allocato dinamicamente, // tramite new, che fornisce il puntatore all'array di puntatori. // Per questo motivo la prima chiamata di new istanzia un oggetto di // tipo CorpoCeleste **, cioe' un puntatore ad un puntatore. const int npp = 8; CorpoCeleste** MieiPianeti = new CorpoCeleste*[npp]; // Con un loop scrivo gli elementi del vettore for (int i=0; i<npp; i++) { MieiPianeti[i] = new CorpoCeleste(" ",i,i,i,i,i); } // Con un loop posso estrarre le informazioni. Ad esempio la massa cout << endl << "Pianeta n. Massa Posizione iniziale " << endl << " x y " << endl << endl; Es1_dbis.cc Prima parte
36 for (int i=0; i<npp; i++) { cout M() X() << " " Y() << endl; } cout << endl; // devo ricordarmi di cancellare gli oggetti allocati dinamicamente // e poiche' ho usato new a due livelli, chiamero' delete 2 volte // 1 - per I puntatori agli oggetti di tipo CorpoCeleste // (cioe' gli oggetti contenuti nell'array di puntatori cout << endl << " Prima del loop con delete MieiPianeti[i] " << endl; for (int i=0; i<npp; i++) { cout << i+1; delete MieiPianeti[i]; } cout << endl << " Dopo il loop con delete MieiPianeti[i] " << endl; // 2 - per l'array di puntatori delete [] MieiPianeti; return 0; } Es1_dbis.cc Seconda parte