Introduzione al C++ e alla programmazione ad oggetti Corso Specialistico CNTC Bologna, 19-23 febbraio 2001 Andrea Dell’Acqua e Claudio Grandi Introduzione al C++ e alla programmazione ad oggetti Introduzione • Le due componenti principali dei programmi: – Algoritmi: l’insieme delle istruzioni che svolgono un particolare compito – Dati: ciò su cui gli algoritmi agiscono per produrre una soluzione unica • La relazione fra queste componenti definisce il paradigma di programmazione – Programmazione procedurale: problemi modellati dagli algoritmi. Dati immagazzinati in aree comuni o passate agli algoritmi – Programmazione ad oggetti: problemi modellati dalle relazioni fra tipi di dati astratti (ADT, Abstract Data Types), chiamati generalmente oggetti Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 2 Il rapporto Dato-Algoritmo Livello di astrazione Programmazione Linguaggio macchina Assemblers Compilatori Linguaggi strutturati Ada (Modula) Object Oriented Introduzione al C++ e alla programmazione ad oggetti Dati Algoritmi Bits Bits Symbolic Words Variables & Types Data structures Abstract Data Types Objects Op-code Statements Subroutines Packages (Modules) Objects 19-23 febbraio 2001 3 Cos’è un oggetto? • Né più né meno di quello che potreste trovare scritto in un vocabolario… – Un oggetto è un’entità che si possa immaginare dotata di determinate caratteristiche e funzionalità. • Lo stato di un oggetto è rappresentato da dati che ne descrivono le caratteristiche in un certo istante • Le funzionalità di un oggetto sono le operazioni che può svolgere quando glie lo si richiede (cioè quando riceve un messaggio) • Nella nostra vita quotidiana siamo molto più abituati a ragionare per oggetti che non in modo strutturato! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 4 Un esempio... Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 5 Soldato Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 6 … cos’è un oggetto: Un insieme di dati e funzioni: Dato Dato Dato Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 7 Incapsulazione • Netta divisione fra interfaccia e implementazione • Da fuori si vede solo l’interfaccia che definisce i messaggi accettati dall’oggetto • I dettagli dell’implementazione (dati e codice delle funzioni) sono invisibili dall’esterno • Ogni oggetto ha in se tutto ciò che gli serve per rispondere alle chiamate (o deve sapere a chi chiedere…) • Il confinamento di informazioni e funzionalità in oggetti permette livelli maggiori di astrazione e semplifica la gestione di sistemi complessi. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 8 Approccio OO • Sono le strutture di dati che svolgono le azioni, non le subroutines • Il lavoro è svolto dal server, non dal client • “Cos’ è?” “Com’ è fatto?” Data Oriented • “Cosa può fare per me?” Object Oriented Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 9 Perché programmare per oggetti? • Programmare per oggetti non velocizza l’esecuzione dei programmi... • Programmare per oggetti non ottimizza l’uso della memoria... E allora perchè programmare per oggetti? • Programmare per oggetti facilita la progettazione e il mantenimento di sistemi software molto complessi! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 10 Caratteristiche del software non mantenibile • Rigidità – non può essere cambiato con faciltà – non può essere stimato l’impatto di una modifica • Fragilità – una modifica singola causa una cascata di modifiche successive – i bachi sorgono in aree concettualmente separate dalle aree dove sono avvenute le modifiche • Non riusabilità – esistono molte interdipendenze, quindi non è possibile estrarre parti che potrebbero essere comuni Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 11 Programmazione ad oggetti • La programmazione ad oggetti, attraverso l’incapsulazione, consente di: – ridurre la dipendenza del codice di alto livello dalla rappresentazione dei dati – riutilizzare del codice di alto livello – sviluppare moduli indipendenti l’uno dall’altro – avere codice utente che dipende dalle interfacce ma non dall’implementazione Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 12 C++ e Object Orientation • Il C++ può essere usato come linguaggio procedurale o per programmazione ad oggetti • Object Orientation implementata attraverso il concetto di classe • Prima di affrontare il problema della programmazione OO con C++ dobbiamo: – capire dove la programmazione procedurale fallisce – affrontare la sintassi del C++ Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 13 Programmazione procedurale • Esempio: cinematica relativistica Idea: perché non usare una function? COMMON /MYDATA/ P1(4), P2(4), + P3(4), P4(4) REAL P1(4), P2(4), P3(4), P4(4) COSTHETA12 = (P1(1)*P2(1) + P1(2)*P2(2) + + P1(3)*P2(3))/... COSTHETA13 = (P1(1)*P3(1) + P1(2)*P3(2) + + P1(3)*P3(3))/... COSTHETA14 = (P1(1)*P4(1) + P1(2)*P4(2) + + P1(3)*P4(3))/... FUNCTION COSTHETA(P1, P2) REAL P1(4), P2(4) COSTHETA = (P1(1)*P2(1) + P1(2)*P2(2) + + P1(3)*P2(3))/... END COMMON /MYDATA/ P1(4), P2(4), + P3(4), P4(4) REAL P1(4), P2(4), P3(4), P4(4) COSTHETA12 = COSTHETA(P1, P2) COSTHETA13 = COSTHETA(P1, P3) COSTHETA14 = COSTHETA(P1, P4) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 14 Evoluzione del codice • Se cambia il formato del common block? COMMON /MYDATA/ P(4), P1(4),E(4), P2(4), THETA(4), P3(4), P4(4) PHI(4) • Bisogna cambiare la funzione (gli argomenti sono FUNCTION COSTHETA1(THETA1, THETA2, diversi) + PHI1, PHI2) COSTHETA1 = SIN(THETA1)*SIN(THETA2) * + COS(PHI1-PHI2) + COS(THETA1)*COS(THETA2) END • ...e il codice! COMMON /MYDATA/ P(4), E(4), + THETA(4), PHI(4) COSTHETA12 = COSTHETA1(THETA(1),THETA(2), + PHI(1), PHI(2)) COSTHETA13 = COSTHETA1(THETA(1),THETA(3), + PHI(1), PHI(3)) COSTHETA14 = COSTHETA1(THETA(1),THETA(4), + PHI(1), PHI(4)) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 15 Il concetto di dipendenza • Nell’esempio precedente il codice di analisi (“alto livello”) dipende dai dettagli della struttura dati (“basso livello”). FUNCTION COSTHETA(P1, P2) REAL P1(4), P2(4) COSTHETA = (P1(1)*P2(1) + P1(2)*P2(2) + + P1(3)*P2(3))/... END COMMON /MYDATA/ P1(4), P2(4), + P3(4), P4(4) COSTHETA12 = COSTHETA(P1, P2) COSTHETA13 = COSTHETA(P1, P3) COSTHETA14 = COSTHETA(P1, P4) Introduzione al C++ e alla programmazione ad oggetti COSTHETA dipende dalla struttura dei dati P1 e P2 Il codice di analisi dipende dalla struttura del common block MYDATA 19-23 febbraio 2001 16 OO riduce le dipendenze! • Riduce la dipendenza del codice di alto livello dalla rappresentazione dei dati Permette il riutilizzo del codice di alto livello • Nasconde i dettagli di implementazione Supporta tipi di dati astratti (vedere seguito ...) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 17 Sintassi: FORTRAN vs C/C++ • Struttura del programma In C/C++ non è necessario un particolare formato il codice PROGRAM TEST C esempio di programma int main() { // esempio di programma ... ... spazi... return 0; // fine END } Il C/C++ è case sensitive INTEGER I INTEGER*4 J REAL X REAL*8 D Introduzione al C++ e alla programmazione ad oggetti Istruzioni separate da “;” int i; long j; float x; double d; 19-23 febbraio 2001 18 Il main program • Ogni programma in C++, per essere eseguibile, deve contenere una funzione main() da cui l’esecuzione comincerà • main() deve avere un tipo (decidere quale è compito del programmatore). Regola generale è che main() ritorni un intero, a significare il return code dell’applicazione int main() { // il piu` semplice programma in C++ return 0; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 19 I/O: lettura e scrittura • Non esiste nel C++ nativo. Si usa: iostream #include <iostream> – Gli operatori << e >> sono usati per definire la direzione del flusso – cin, cout e cerr rappresentano lo standard input, output e error del programma #include <iostream> int main() { cout << “Hello, world !” << endl; return 0; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 direttiva al preprocessore end of line 20 Commenti • Esistono due tipi di commento in C++ – inline: const int Ntries; // questo e` un commento inline // il resto della linea e’ trattato come un commento – multiline (come in C): const int Ntries; /* questo e` un commento multiline: tutto viene trattato come un commento fino a quando il commento stesso non viene chiuso con uno */ – I due tipi possono essere usati indifferentemente, ma si raccomanda di usare l’inline (più semplice e meno ambiguo) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 21 Tipi predefiniti in C++ • Sono definiti una serie di tipi numerici che permettono di rappresentare numeri interi, reali e caratteri int long float double long double unsigned int unsigned double char bool intero in singola precisione intero in doppia precisione reale in singola precisione reale in doppia precisione reale in precisione estesa intero senza segno reale senza segno in doppia precisione carattere singolo variabili logiche – char (un solo byte) viene normalmente usato per rappresentare interi inferiori a 256 – stringhe e numeri complessi sono implementati come tipi derivati Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 22 Tipi predefiniti in C++ (2) Costanti carattere Esempi di costanti 123 123 0x123 123l 123u ‘A’ 3.14f ‘1’ ‘\t’ 3.1415 3.1415L 300e-2 .03e2 interi costanti, decimale, ottale, esadecimale interi, long, unsigned caratteri, tab float, double, long double double, notazione esponenziale stringa costante boolean 30e-1 “Nome” true false ‘\a’ ‘\\’ ‘\b’ ‘\r’ ‘\”’ ‘\f’ ‘\t’ ‘\n’ ‘\0’ ‘\’’ ‘\v’ ‘\101’ ‘\x041’ alert backslash backspace carriage return double quote form feed tab newline carattere nullo single quote vertical tab 101 ottale, ‘A’ esadecimale, ‘A’ Stringhe costanti “” “nome” “una \”stringa\”” “una stringa \ su piu` linee” Introduzione al C++ e alla programmazione ad oggetti stringa nulla (‘\0’) ‘n’ ‘o’ ‘m’ ‘e’ ‘\0’ stampa: una “stringa” un \ alla fine della linea per continuare la stringa 19-23 febbraio 2001 23 Tipi predefiniti in C++ (3) OS OS OS 16 bit 32 bit 64 bit char[1] 8 8 8 int[1] 16 32 32 bool 16 32 32 short[1] 16 16 16 long[1] 32 32 64 float 32 32 32 double 64 64 64 long double 64 128 Introduzione al C++ e alla programmazione ad oggetti 128 [1] Può essere unsigned 19-23 febbraio 2001 24 Identificatori • Un identificatore è composto da uno o più caratteri • Il primo carattere deve essere una lettera o un underscore. Caratteri successivi possono essere lettere, numeri o underscore const int Ntries; double _attempts; double 2A; // errore! • Non c’ è un limite in lunghezza, anche se alcuni sistemi si limitano a considerare i primi 31 caratteri • Gli identificatori che iniziano con un doppio underscore o con un underscore e una lettera maiuscola sono riservati ad usi di sistema • C++ e` case sensitive! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 25 Keywords • Alcuni identificatori sono esplicitamente riservati al sistema (hanno un preciso significato in C++) e non possono essere usati asm auto bool break case catch char class const const_cast continue default delete do double dynamic_cast else enum explicit extern false float for friend goto if inline int long mutable namespace new Introduzione al C++ e alla programmazione ad oggetti keyword operator private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this 19-23 febbraio 2001 throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while 26 const • La keyword const viene utilizzata per dichiarare un oggetto costante Esempi di const const int N=100; double w[N]; const int vect[5]= {10,20,30,40,50}; N non puo` essere cambiato N usato come per dimensionare un vettore le componenti di vect non possono essere cambiate • In C le costanti vengono normalmente dichiarate usando il preprocessore #define N 100 – in questo caso N e` una costante senza tipo ed il preprocessore sostituisce N ovunque lo trovi nel programma, senza rispettare le regole di scope (da evitare) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 27 Dichiarazione • Le dichiarazioni associano un significato ad un identificatore • in C++ ogni cosa deve essere dichiarata per poter essere usata const int i; double max(double r1,double r2); // la variabile i // la funzione max • Una dichiarazione è spesso anche una definizione. Per variabili semplici questo consiste nell’associare un valore alla variabile al momento della dichiarazione const double pi=3.1415926; double max(double r1, double r2) { return (r1>r2) ? r1: r2; } Introduzione al C++ e alla programmazione ad oggetti // definizione // dichiarazione // definizione di max 19-23 febbraio 2001 28 typedef • L’istruzione typedef viene utilizzata per creare un alias per tipi esistenti typedef int INTEGER; typedef int BOOLEAN; typedef void (*ptr_f)(); // // // // // per i nostalgici del fortran usato prima che bool venisse implementato ptr_f e` un puntatore ad una procedura (subroutine) • typedef NON può essere usato per implementare nuovi tipi, ma solo per definire un alias typedef mela frutto; Introduzione al C++ e alla programmazione ad oggetti // compila soltanto se mela // e` gia` stata definita 19-23 febbraio 2001 29 Enumeratori • In C++ sono supportati tipi definiti dall’utente enum Color { red, green, blue }; Color screenColor = blue; Color windorColor = red; int n = blue; // valido Color c = 1; // errore enum Seme { cuori, picche, quadri, fiori }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 30 Scope • Le variabili possono essere dichiarate e definite quasi ovunque in un programma in C++ • la visibilità (scope) di una variabile dipende da dove la variabile è stata dichiarata int func() { … const int n=50; for (int i=0;i<100;i++) { double r; ... } cout<<“n “<< n <<endl; cout<<“i “<< i <<endl; cout<<“r “<< r <<endl; … } Introduzione al C++ e alla programmazione ad oggetti // function scope // i e` locale // r e` locale // OK // errore! Ma... // errore! 19-23 febbraio 2001 31 Scope (2) • Attenzione! La stessa variabile può essere ridichiarata (con visibilità diversa). Questo è da evitare (se possibile) per non rendere il programma oscuro e a rischio di errore! int i; int func() { int i=50; // file (global) scope // function scope, nasconde // la i a file scope for (int i=0;i<100;i++) // block scope. Nasconde // la i a function scope { int i; // questo e` un errore... ... } cout<<“i “<< i <<“ “<< ::i <<endl; ... } Introduzione al C++ e alla programmazione ad oggetti Scope resolution operator 19-23 febbraio 2001 32 namespace • Funzioni e variabili definite a global scope sono visibili dappertutto in un programma in C++ – Per evitare che funzioni diverse (definite in librerie diverse) con lo stesso nome possano interferire (name clash), C++ implementa il concetto di namespace, che introduce un ulteriore, più alto livello di scope namespace mynames { int i; float max(float, float); } // la mia dichiarazione di i // la mia dichiarazione di max float mynames::max(float a, float b) // implementazione della { // funzione max appartenente return (a>b) ? a : b; // al namespace mynames } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 33 namespace (2) • Per utilizzare variabili e funzioni racchiuse in un namespace si può: – o accedere all’intero namespace using namespace mynames; ... float r = max (2.1f, 5.3f); – oppure accedere alla singola variabile o funzione float r = mynames::max (2.1f, 5.3f); – oppure dichiarare la singola funzione using mynames::max; ... float r = max (2.1f, 5.3f); Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 34 Operatori Espressioni Aritmetiche Auto-incremento e decremento k k k k = = = = ++j; j++; --j; j--; +w a/b a+b a-b i%2 a=3; Espressione j=j+1; k=j; k=j; j=j+1; j=j-1; k=j; k=j; j=j-1; bit-wise ~i; i&j; i|j i^j i<<n i>>n -i a*b significato piu` e meno unari moltiplicazione, divisione, modulo addizione e sottrazione binarie assegnazione Operatori relazionali < > <= >= == != ! && || Complemento bit a bit AND bit a bit OR bit a bit XOR bit a bit shift a sinistra di n pos. shift a destra di n pos. Introduzione al C++ e alla programmazione ad oggetti Commento minore di maggiore di minore o uguale maggiore o uguale uguale diverso Negazione unaria and logico or logico 19-23 febbraio 2001 Fortran .LT. .GT. .LE. .GE. .EQ. .NE. .NOT. .AND. .OR. 35 Espressioni di assegnazione • Le espressioni di assegnazione sono valutate da destra a sinistra a = j++; j viene incrementato ed il risultato assegnato ad a • Le assegnazioni multiple sono permesse a = b = c = d = 100; • alcuni operatori di assegnazione combinano assegnazione ed altri operatori a *= b; a -= b; // equivale ad // equivale ad a = a*b; a = a-b; • Assegnazioni possono essere fatte all’interno di espressioni aritmetiche a = b + ( c = 3 ); Introduzione al C++ e alla programmazione ad oggetti // equivale a c=3; a=b+c; 19-23 febbraio 2001 36 Statements Statement C++ commenti vuoto espressione composto ; j=j+k; { . . . . } goto if goto label; if (p==0) cerr<<“error”; if (x==y) cout<<“the same”; else cout<<“different”; for (j=0;j<n;j++) a[j]=0; while (i != j) i++; do y=y-1; while (y>0); break; continue; if-else for while do-while break continue Introduzione al C++ e alla programmazione ad oggetti usato in funzioni, if.. Costituisce un blocco da non usarsi un solo branch due branch le dichiarazioni sono permesse 0 o piu` iterazioni 1 o piu` iterazioni esce dal blocco prossima iterazione 19-23 febbraio 2001 37 Statements (2) Statement switch C++ commenti dichiarazione switch (s) { case 1: ++i; case 2: --i; default: ++j; }; int i=7; try try {. . . .} label error: cerr<<“Error!”; return x*x*x; return Introduzione al C++ e alla programmazione ad oggetti si deve usare break per evitare di cadere nei casi successivi e aggiungere un caso di default alla fine della lista in un blocco, file o namespace usato per trattare le eccezioni usato con goto valore di ritorno di una funzione 19-23 febbraio 2001 38 Statement composti • Uno statement composto in è costituito da una serie di statement contenuti fra parentesi graffe • Usato normalmente per raggruppare istruzioni in un blocco (if, for, while, do-while, etc.) • Il corpo di una funzione è sempre uno statement composto • La dichiarazione di una variabile può avvenire ovunque all’interno di un blocco, in questo caso lo scope della variabile sarà il blocco stesso • Ovunque si possa usare uno statement singolo si può definire un blocco Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 39 if • Attenzione all’uso di = e == if (i=1) {. . . .} // questo e` sempre vero!!! • Nel dubbio, usare sempre un blocco… if (i != 0) a++; a/=i; // possibile divisione per 0 // mancano delle {}? • Attenzione agli else! if (i == 0) if (a<0) { // possibile divisione per 0 cerr<<“a e` negativo!”; } else b=a/i; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 40 while e do-while • La forma generale di un while è : while (condizione) statement; • Lo statement verrà eseguito fino a quando la condizione verrà verificata (true). A seconda del volore della condizione, lo statement verrà eseguito zero o più volte • la sintassi di un do-while è invece: do statement; while (condizione); • Lo statement verrà quindi eseguito almeno una volta Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 41 break e continue • break e continue sono utilizzati nei loop per saltare alla fine del loop o fuori dal loop stesso int i,n=0; int a[100]; cin>>i; // leggo il valore di i while (1) // loop infinito { if (i<0) break; if (n>=100) continue; a[n]=i; n++; // continue salta qui } // break salta qui • break e continue possono solamente essere utilizzati nel corpo di un for, while o dowhile. break e` anche usato negli switch Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 42 switch • Lo switch è uno statement condizionale che generalizza lo if-else switch (condizione) (statement); • lo statement è generalmente composito e consiste di diversi case e, opzionalmente, di un default switch (n) { case 0: cout<<“ n e` nullo”<<endl; break; case 1: case 3: case 5: case 7: case 9: cout<<“ n e` dispari”<<endl; break; case 2: case 4: case 6: case 8: case 10: cout<<“ n e` pari”<<endl; break; default: cout<<“ n non e` compreso tra 0 e 10”<<endl; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 43 switch (2) • Non si puo` dichiarare una variabile in uno dei case switch (k) case 0: int . . case 1: . . } { j=0; . // Illegale! Errore! . • … ma si puo` creare una variabile locale definendo uno statement composto... switch (k) { case 0: { int j=0; . . . } case 1: . . . Introduzione al C++ e alla programmazione ad oggetti } // OK, questo compila 19-23 febbraio 2001 44 L’operatore ? • L’operatore ? e` l’unico esempio di operatore ternario in C++ expr1 ? expr2 : expr3; – Equivale a: if(expr1) expr2; else expr3; – Esempio: double max(double a, double b) { double max = (a>b) ? a : b; return max; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 45 Sintassi: FORTRAN vs C/C++ • Controllo di flusso del programma DO I = 1, 10 . . . ENDDO IF (I.EQ.10 .AND. J.GT.4 .OR. X) THEN . . . ENDIF DO WHILE(X .NE. 5) . . . ENDDO Introduzione al C++ e alla programmazione ad oggetti for (i = 1; i <= 10; i++) { . . . } if (i == 10 && j > 4 || x) { . . . } while( x != 5 ) { . . . } 19-23 febbraio 2001 46 Funzioni matematiche • In C++ non esistono funzioni predefinite #include <iostream> #include <cmath> cmath.h definisce sin, cos, ... int main() { double r, theta, phi; cin >> r >> theta >> phi ; double x = r * sin( theta ) * sin( phi ); double y = r * sin( theta ) * cos( phi ); double z = r * cos( theta ); cout << x << “, “ << y << “, “ << z << endl; return 0; } • Potenze: pow(b,exp) (non si può usare ** ) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 47 Array • Sono supportati gli array di dimensione fissa int main() { int x[10]; for ( int i = 0; i < 10, i++ ) x[i] = 0; double m[5][5]; for ( int i = 0; i < 5; i++ ) for ( int j = 0; j < 5; j++ ) m[i][j] = i * j; return 0; } • Inizializzazione: int x[] = { 1, 2, 3, 4 }; char[] t = { ‘C’, ‘i’, ‘a’, ‘o’, ‘\0’ }; char[] s = “Ciao”; int m[2][3] = { {11, 12, 13}, {21, 22, 23} }; • L’indice va da 0 a n-1. Usare un indice maggiore di n-1 può causare un crash. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 48 Esempio con gli arrays • Moltiplicazione fra matrici: int main() { const int DIM=3; float m[DIM][DIM], m1[DIM][DIM], m2[DIM][DIM]; // Assumiamo che m1 ed m2 vengano riempiti qui... // Moltiplicazione: for (int i=0; i<DIM; i++) { for (int j=0; j<DIM; j++) { float sum=0; for (int k=0; k<DIM; k++) sum += m1[i][k] * m2[k][j]; m[i][j] = sum; } } return 0; Introduzione al C++ e alla programmazione ad oggetti } 19-23 febbraio 2001 49 Puntatori • Riferimento ad una locazione di memoria #include <iostream> ptr j 24 12 int main() { int j = 12; int *ptr = &j; cout << *ptr << endl; j = 24; cout << *ptr << endl; cout << ptr << endl; return 0; } 12 24 0x7b03a928 Introduzione al C++ e alla programmazione ad oggetti indirizzo di memoria 19-23 febbraio 2001 50 Puntatori • Puntatore nullo #include <iostream> ptr j 12 int main() { int j = 12; int *ptr = 0; cout << *ptr << endl; // crash ! return 0; } Segmentation violation (core dumped) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 51 Puntatori e array • In C gli array sono trattati come puntatori X[0] 1.5 X[1] 2.5 x X+1 int main() { float x[5]; int j; for (j = 0; j < 5; j++) x[j] = 0; float *ptr *ptr = *(ptr+1) = *(ptr+3) = X[2] 0.0 X[3] 3.5 X[4] 0.0 X+3 = x; 1.5; // x[0] = 1.5 2.5; // x[1] = 2.5 3.5; // x[3] = 3.5 } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 52 Puntatori: allocazione dinamica • Riferimento ad una locazione di memoria #include <iostream> ptr 12 int main() { int *ptr = new int; *ptr = 12; cout << *ptr << endl; delete ptr; return 0; } • Attenzione: – Non usare delete fa accumulare locazioni di memoria inutilizzate (memory leak) – Utilizzare puntatori prima del new o dopo il delete causa il crash del programma Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 53 Puntatori: allocazione dinamica • Riferimento a più locazioni di memoria #include <iostream> int main() { int *ptr = new int[3]; ptr[0] = 10; ptr[1] = 11; ptr[2] = 12 delete [] ptr; return 0; } ptr 10 Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 11 12 54 new e delete • Gli operatori new and delete vengono utilizzati per allocazione/deallocazione di memoria dinamica – la memoria dinamica (heap), è un’area di memoria libera provvista dal sistema per quegli oggetti la cui durata di vita è sotto il controllo del programmatore operatore new int *i=new int; char *c=new char[100]; int *i=new int(99); char *c=new char(‘c’); int *j=new int[n][4]; commenti alloca un alloca un caratteri alloca un alloca un alloca un intero, returna il puntatore array (stringa) di 100 intero e lo inizializza a 99 carattere inizializzato a c array di puntatori ad intero • new riserva la quantità necessaria di memoria richiesta e ritorna l’indirizzo di quest’area Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 55 new e delete (2) • L’operatore delete è usato per restituire una certa area di memoria (allocata con new) allo heap • Ogni oggetto allocato con new deve essere distrutto con delete se non viene piu` utilizzato, altrimenti l’area di memoria che esso occupata non potra` piu` essere ri-allocata (memory leak) • L’argomento di delete è tipicamente un puntatore inizializzato preventivamente con new operatore delete delete ptr; delete p[i]; delete [] p; Introduzione al C++ e alla programmazione ad oggetti commenti distrugge un puntatore ad un oggetto distrugge l’oggetto p[i] distrugge ogni oggetto di tipo p 19-23 febbraio 2001 56 new e delete (3) • Attenzione – la dimensione dello heap non e` infinita – l’allocazione con new può fallire, nel qual caso new restituisce un puntatore nullo o suscita un’eccezione. Nel caso di allocazione di memoria importante bisogna verificare che l’operazione abbia avuto successo prima di usare il puntatore – ogni oggetto creato con new deve essere distrutto con delete, ogni oggetto creato con new [] deve essere distrutto con delete [] , queste forme NON sono intercambiabili Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 57 Regole di conversione e cast • In C++ esistono conversioni esplicite ed implicite. – Le conversioni implicite (e.g. intfloat) nelle espressioni aritmetiche, nel passare i parametri ad una funzione o nel ritornare un valore da una funzione rendono il meccanismo di conversione molto conveniente ma anche potenzialmente pericoloso (errori a run time) Conversioni implicite •char, short e bool vengono promossi ad int •Tipi interi che non possono essere rappresentati con un int vengono promossi a unsigned •In una espressione di tipo misto, gli operandi di ordine inferiore vengono promossi all’ordine superiore secondo la gerarchia: int<unsigned<long<unsigned long<float<double<long double •bool e` un tipo intero, con true che viene promosso a 1 e false a 0 Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 58 Regole di conversione e cast (2) • Ogni genere di puntatore può essere convertito in un puntatore generico a void • Al contrario di quanto avviene in C, un puntatore generico non è compatibile con un puntatore di tipo arbitrario ma richiede un cast esplicito char *ch; void *generic_p; . . . generic_p=ch; // OK, char* va in void* ch=generic_p; // OK in C, illegale in C++ ch=(char *)generic_p; // OK, C e C++ arcaico • Ogni puntatore puo` essere inizializzato a 0 senza bisogno di un cast esplicito. • In C++ usare 0 e non NULL per i puntatori! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 59 Casting in ANSI C++ • Data la complessità delle operazioni di casting in C++ nuovi operatori di casting sono stati aggiunti a quelli già esistenti in C Cast commenti x=(float) i; cast in C++ - notazione C x=float(i); cast in C++, notazione funzionale x=static_cast<float>(i); ANSI C++ - raccomandato i=reinterpret_cast<int>(&x) ANSI C++, non portabile e system dependent dove C_var e` una variabile dichiarata const. Usato per eliminare la “const-ness” per chiamare func func(const_cast<int>(c_var)) • Esiste anche un dynamic_cast, utilizzato per riconoscere il tipo di un oggetto a run-time (RTTI) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 60 Funzioni • In C++ le funzioni sono caratterizzate da un nome, dal tipo della variabile ritornata e da una lista di parametri (opzionali) Tipo ritornato Corpo della funzione Parametri double max( double a, double b) { return (a>b) ? a : b; } Valore di ritorno • La lista dei parametri (anche se vuota) deve essere esplicitata • Il valore ritornato deve essere compatibile, a meno di conversione esplicita, con il tipo della funzione Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 61 Funzioni (2) Tipo di dichiarazione C++ commenti funzione double cube(double x) { return x*x*x; } parametri passati “by value” procedura void pr_square(int i) { cout<<i*I<<endl; } subroutine, non si usa return senza argomenti void hello () { cout<<“Hello”<<endl; } puo` anche essere void hello(void) argomenti passati per riferimento void swap(int& i,int& j) { int t=i; i=j; j=t; } i e j hanno i loro valori scambiati variabile int scanf(const char, inline inline double cube(int x) codice inline argomenti di default int power(int i, int n=2) il 2do argomento puo` essere tralasciato Introduzione al C++ e alla programmazione ad oggetti …) chiamata con un qualsiasi numero di argomenti 19-23 febbraio 2001 62 Prototipi delle funzioni • Prima di essere usata, una funzione deve essere dichiarata (nel file che la usa) main.cc max.cc #include <iostream> double max (double a, double b) double max(double, double); { int main() return (a>b) ? a : b; { } double m = max(1, 3); cout<<“Il massimo e` “<<m<<endl; Prototipo di max return 0; } (normalmente in max.h) • I prototipi rendono le funzioni in C++ “type safe”, nel senso che i valori reali degli argomenti vengono all’occorrenza convertiti nei tipi formali specificati dal prototipo Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 63 Call-by-Reference • L’uso dei riferimenti permette ad una funzione di modificare il valore dei suoi argomenti bool greater(int& i, int& j) { // se i>j scambia i e j if (i>j) { int temp=i; Argomenti passati “by reference” i=j; possono essere modificati dalla j=temp; funzione stessa return true; } else return false; } – Per ragioni di efficenza, oggetti di grandi dimensioni (in termini di memoria) vengono normalmente passati “by reference”. – Per evitare che possano essere modificati dalla funzione, il riferimento viene definito const Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 64 Funzioni inline • La keyword inline suggerisce al compilatore che ogni chiamata alla funzione deve essere convertita in codice eseguibile (la definizione della funzione viene sostituita alla chiamata dovunque nell codice) • Le funzioni inline vengono usate per ragioni di efficienza e (per non sovraccaricare il compilatore) devono essere semplici • Il compilatore può decidere autonomamente (per esempio se la funzione è troppo lunga) di ignorare la direttiva inline Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 65 Argomenti di default • Ad ogni parametro di una funzione può essere assegnato un valore di default. Questo permette di chiamare la funzione tralasciando quei parametri il cui valore di default risulta appropriato main.cc pow.cc int pow(int , int); int main() { int r=3; int a1=pow(3,3); int a2=pow(3); return 0; } // a1=27 // a2=9 int pow (int a, int k=2) { if (k==2) return a*a; else return a*pow(a, k-1); } Argomento di default • Solo ai parametri più a destra nella calling sequence può essere dato un default. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 66 Overloading • Funzioni diverse possono avere lo stesso nome • La funzione che viene chiamata è scelta dal compilatore in base al tipo di ritorno ed al numero e tipo degli argomenti average_array.cc double average_array(const int a[], int size) { int sum=0; for (int i=0;i<size;i++) sum+=a[i]; return double(sum)/size; } double average_array(const double a[], int size) { double sum=0; for (int i=0;i<size;i++) sum+=a[i]; return sum/size; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 67 Overloading (2) • La lista dei tipi degli argomenti di una funzione è chiamata signature • Il tipo ritornato dalla funzione non fa parte della signature, mentre il numero e l’ordine degli argomenti è cruciale void print(int i=0) {. . .} // (1) void print(int i, double x) {. . .} // (2) void print(double y, int i) {. . .} // (3) . . . print(‘A’); // ‘A’ e` convertito a int, chiama (1) print(str[]); // errore! Non e` possibile una conversione print(15,9); // errore! Ambiguita` fra (2) e (3) print(15,9.); // OK, chiama (2) print(); // OK, chiama (1) con il default Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 68 L’algoritmo di selezione Ricerca della corrispondenza esatta Promozioni standard degli argomenti int long Conversioni standard dei tipi int float Conversioni definite dall’utente traccia int Corrispondenza con l’ellipsi (…) • L’utente può sempre utilizzare una conversione forzata (type cast) per ottenere una corrispondenza • Il compilatore segnala tutti i casi in cui esiste ambiguità Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 69 Funzioni esterne • Si possono chiamare funzioni FORTRAN da C++: SUBROUTINE HBOOK1(ID, TITLE, NBIN, MIN, MAX, OPT) SUBROUTINE HFILL(ID,X, Y, WEIGHT) extern “C” void hbook1_(int&, char*, int&, float&, float&, float&, int); extern “C” void hfill_(int&, float&, float&, float&); ... hbook1_(100, title, ……) // BUS ERROR!!! (il FORTRAN passa // sempre “by-reference” int id=100; hbook1_(id, title, ……) Introduzione al C++ e alla programmazione ad oggetti // OK! 19-23 febbraio 2001 70 Parametri del programma • Dotando main() di una lista di argomenti, è possibile avere accesso ai parametri passati dalla command line: #include <iostream.h> int main(int argc, char *argv[]) { cout<<“ argc e`: “<<argc<<endl; cout<<“ il nome dell’eseguibile e` “<<*argv<<endl; for (int i=1; i<argc; i++) cout<<“Argomento #”<<i<<“ = “<<*(argv+i)<<endl; return 0; } • argc è il numero di parametri passati dalla command line (sempre almeno 1, il nome del programma) mentre il vettore di stringhe argv contiene ogni singolo parametro Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 71 Parametri del programma (2) • Lanciato con il comando prompt> mytest questo e un test • il programma produrra` il seguente output: argc e` : 5 il nome dell’eseguibile e`/user/andrea/myprogram Argomento #1 = questo Argomento #2 = e Argomento #3 = un Argomento #4 = test Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 72 Organizzazione dei files • Normalmente, le dichiarazioni delle interfacce e le specifiche sono separate dall’implementazione – header files (.h o .hh) • inclusi nei file sorgente utilizzando direttive del precompilatore #include <iostream.h> • non contengono codice eseguibile (con l’eccezione delle definizioni delle funzioni inline) • non devono essere inclusi piu` di una volta, per evitare problemi con il linker #ifndef MyHeader_H #define MyHeader_H // dichiarazioni ….. #endif Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 73 Organizzazione dei files (2) – Files sorgente (.C,.cxx,.cpp,.cc) • contengono l’implementazione di funzioni e metodi • codice eseguibile • includono gli header files utilizzando le direttive del preprocessore • vengono compilati – Funzioni inline (.icc) • La definizione di una funzione inline deve essere visibile là dove viene usata. • Normalmente implementate negli header files o in files separati (con estensione .icc) che devono essere inclusi nel files sorgente che ne facciano uso Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 74 C++ e Object Orientation • Definizione di nuovi tipi (oltre a int, float, double) come: • numeri complessi, • vettori, • matrici, . . . • ma anche: • traiettorie, • superfici, • elementi di apparati sperimentali,... • Gli oggetti permettono di modellare una problema che rappresenti la realtà Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 75 …C++ e Object Orientation • Object Orientation implementata in C++ attraverso il concetto di classe: • I dati privati (o attributi) di una classe definiscono lo stato dell’oggetto • Le funzioni (o metodi) di una classe implementano la risposta ai messaggi Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 76 Una classe C++ Attributo Attributo Attributo Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 77 Classe Vector2D • Un esempio: un vettore bidimensionale Vector2D.h costruttore class Vector2D { public: Vector2D(double x, double y); double x(); double y(); Vector2D.cc double r(); double phi(); #include “Vector2D.h” private: Vector2D::Vector2D(double x, double y): x_(x), double x_; y_(y) { double y_ } }; double Vector2D::x() { return x_; } funzioni o metodi dati o attributi Punto e virgola! double Vector2D::r() { return sqrt( x_*x_ + y_*y_); } ... Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 78 Interfaccia e implementazione • Gli attributi privati non sono accessibili al di fuori della classe • I metodi pubblici sono Vector2D.cc gli unici visibili #include “Vector.h” Vector2D.h class Vector2D { public: Vector2D(double x, double y); double x(); double y(); double r(); double phi(); private: double x_; double y_; }; Introduzione al C++ e alla programmazione ad oggetti Vector2D::Vector2D(double x, double y) : x_(x), y_(y) {} double Vector2D::x() { return x_; } double Vector2D::r() { return sqrt(x_*x_ + y_*y_); } 19-23 febbraio 2001 79 Costruttori e distruttori • Un costruttore è un metodo il cui nome è quello della classe a cui appartiene • Lo scopo di un costruttore è quello di costruire oggetti del tipo della classe. Questo implica l’inizializzazione degli attributi e, frequentemente, allocazione di memoria dallo heap • Un costruttore la cui lista di argomenti è vuota o composta di argomenti di default viene normalmente chiamato costruttore di default Vector2D::Vector2D() {. . . .} // costruttore di default #include “Vector2D.h” . . . Vector2D v; // oggetto costruito con il // costruttore di default Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 80 Costruttori e distruttori (2) • Un costruttore del tipo che ha come argomento un riferimento ad un oggetto della stessa classe viene chiamato copy constructor Vector2D::Vector2D(const Vector2D& v) {. . . .} • Il copy constructor viene normalmente utilizzato: – quando un oggetto è inizializzato per assegnazione Vector2D v(v1); // dove v1 e` di tipo Vector2D – quando un oggetto è passato come argomento ad una funzione – quando un oggetto è ritornato da una funzione • Se non viene fornito esplicitamente dall’utente, il compilatore ne genererà uno automaticamente Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 81 Costruttori e distruttori (3) • Gli attributi di una classe possono essere inizializzati nel costruttore per mezzo di una lista di inizializzatori, che precede il corpo della funzione Vector2D::Vector2D(double x, double y) : x_(x), y_(y) { . . . } • Quando uno degli attributi è esso stesso una classe, il costruttore appropriato viene scelto sulla base dei parametri forniti nell’inizializzazione • E` obbligatorio inizializzare gli attributi (non statici) che siano o riferimenti o const Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 82 Costruttori e distruttori (4) • Il distruttore è un metodo il cui nome è quello della classe a cui appartiene preceduto da una tilde (~) • Il distruttore viene chiamato automaticamente quando un oggetto sta per essere distrutto (sia perchè delete è stato invocato sia perchè l’oggetto è finito fuori scope • Il compito del distruttore è di assicurarsi che l’oggetto per cui è invocato verrà distrutto senza conseguenze. In particolare, se memoria è stata allocata nel costruttore, il distruttore dovrà assicurarsi di restituirla allo heap Vector2D::~Vector2D() Introduzione al C++ e alla programmazione ad oggetti {} // vuoto, in questo caso 19-23 febbraio 2001 83 Costruttori e distruttori (5) • I costruttori con un solo parametro sono automaticamente trattati come operatori di conversione Vector2D::Vector2D(int i) {. . .} // costruisce un vettore a partire da un intero, ma puo` // essere usato per convertire un intero in vettore v=Vector2D(i); • Per evitare la conversione si puo` usare explicit explicit Vector2D(int); Introduzione al C++ e alla programmazione ad oggetti // solo costruttore 19-23 febbraio 2001 84 Classe Vector2D • Come usare Vector2D: main.cc #include <iostream.h> #include “Vector2D.h” invoca il constructor int main() { Vector2D v(1, 1); cout << “ v = << v.x() << v.y() cout << “ r = cout << “ phi return 0; (“ << “,” << “)” << endl; “ << v.r(); = “ << v.phi() << endl; } Output: v = (1, 1) r = 1.4141 phi = 0.7854 Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 85 Classe Vector2D • … oppure attraverso un puntatore... main.cc #include <iostream.h> #include “Vector2D.h” Allocazione sullo heap int main() { Vector2D *v = new Vector2D(1, 1); cout << “ v = (“ << v->x() << “,” << v->y() << “)” << endl; cout << “ r = “ << v->r(); cout << “ phi = “ << v->phi() << endl; delete v; return 0; } Attenzione! Introduzione al C++ e alla programmazione ad oggetti Output: v = (1, 1) r = 1.4141 phi = 0.7854 19-23 febbraio 2001 86 Interfaccia e implementazione • La struttura interna dei dati (x_, y_) che rappresentano l’oggetto della classe Vector2D sono nascosti (private) agli utilizzatori della classe. • Gli utilizzatori non dipendono dalla struttura interna dei dati (come lo erano gli utilizzatori dei common blocks Fortran) • Se la struttura interna cambia (es.: r_, phi_), il codice che usa Vector2D non deve essere modificato. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 87 Classe Vector2D • Protezione dell’accesso ai dati: main.cc #include <iostream> #include “Vector2D.h” int main() { Vector2D v(1, 1); cout << << << cout << cout << “ V = (“ v.x_ << “,” // v.y_ << “,” << endl; // non compila ! “ r = “ << v.r(); “ phi = “ << v.phi() << endl; } • I metodi di una classe hanno libero accesso ai dati privati e protetti di quella classe Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 88 Selettori e modificatori • Selettore: metodo che non modifica lo stato (attributi) della classe. E’ dichiarato const • Modificatore: metodo che può modificare lo stato Vector2D.cc della classe Vector2D.h #include “Vector2D.h” class Vector2D { void Vector2D::scale(double s) public: { Vector2D(double x, double y); x_ *= s; y_ *= s; double x() const; } double y() const; double r() const; Selettori (const) main.cc double phi() const; void scale(double s); #include “Vector2D.h” private: int main() double x_, y_; modificatore { }; const Vector2D v(1, 0); double r = v.r() // OK v.scale( 1.1 ); // errore! } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 89 friend • La keyword friend puo` essere usata perche` una funzione (o una classe) abbia libero accesso ai dati privati di un’altra classe class A { . . . friend int aFunc(); friend void C::f(int); }; class B { … friend class C; }; class C { . . . }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 90 friend (2) • friend (nonostante il nome) e` nemico dell’incapsulamento e quindi dell’Object Orientation • Un uso eccessivo di friend è quasi sempre sintomo di un cattivo disegno • Esistono anche situazioni in cui un friend può essere accettabile – Overloading di operatori binari – Considerazioni di efficienza – Relazione speciale fra due classi “A programmer must confer with an architect before making friend declarations” Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 91 static • Attributi dichiarati static in una classe sono condivisi da tutti gli oggetti di quella classe • Metodi dichiarati static non possono accedere ad attributo non statici della classe • Attiributi statici possono essere usati e modificati soltanto da metodi statici • Nonostante l’utilizzo di static sembri imporre condizioni troppo restrittive, esso risulta utile nell’implementazione di: – contatori – singleton (vedi oltre) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 92 Un contatore Class MyClass { private: static int counter; static void increment_counter() { counter++; } static void decrement_counter() { counter--; } public: MyClass() { increment_counter(); } ~MyClass() { decrement_counter(); } static int HowMany() { return counter; } }; Un membro statico deve essere #include <iostream.h> inizializzato una e una sola volta #include “MyClass.h” int MyClass::counter=0; nel codice eseguibile Un metodo statico puo` essere int main() { invocato cosi`... MyClass a,b,c; MyClass *p=new MyClass; cout<<“ How many? “<< MyClass::HowMany() <<endl; delete p; cout<<“ and now? “<< a.HowMany() <<endl; return 0; } … o cosi`... Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 93 Un singleton • Un singleton è una classe di cui, ad ogni momento nel corso del programma, non può esistere più di una copia (istanza) class aSingleton { Pattern utile per private: static aSingleton *ptr; l’implementazione di aSingleton () {} classi “manager” di cui public: static aSingleton *GetPointer(){ deve esistere una sola if (ptr==0) istanza ptr=new aSingleton; return ptr; #include “aSingleton.h” } }; aSingleton *aSingleton::ptr=0; Attenzione a non farlo diventare l’equivalente di un common block! Introduzione al C++ e alla programmazione ad oggetti int main() { aSingleton *mySing= aSingleton::GetPointer(); . . . Return 0; 19-23 febbraio 2001 94 } Operatori • E’ possibile ridefinire +, -, *, [], ++, ==, . . . Vector2D.h class Vector2D { public: Vector2D(double x, double y); double x() const; double y() const; double r() const; double phi() const; private: double x_; double y_; }; Vector2D operator+(const const Vector2D operator-(const const Vector2D& Vector2D& Vector2D& Vector2D& Introduzione al C++ e alla programmazione ad oggetti Vector2D.cc Vector2D operator+(const const { return Vector2D(v1.x() v1.y() } v1, v2); v1, v2); Vector2D operator-(const const { return Vector2D(v1.x() v1.y() } 19-23 febbraio 2001 Vector2D& v1, Vector2D& v2) + v2.x(), + v2.y()); Vector2D& v1, Vector2D& v2) - v2.x(), - v2.y()); 95 Operatori (2) • Esempio: main.cc #include <iostream> #include “Vector2D.h” int main() { Vector2D v1(1, 0), v2(0, 1); Vector2D v; Sintassi alternativa (!#@!?) : v.operator=( operator+(v1, v2) ); v = v1 + v2; cout << “ v = “ << v << endl; cout << “ r = “ << v.r(); cout << “ phi = “ << v.phi() << endl; } Output: ridefinizione di << v = (1, 1) r = 1.4141 theta = 0.7854 Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 96 Operatori (3) • Esempio: main.cc #include #include #include #include <iostream> <cmath> “Vector3D.h” “Matrix.h” // matrice 3x3 p greco int main() { Vector3D v1(1, 1, 0); double phi = M_PI/3; double c = cos(phi), s = sin(phi); Matrix m(1, 0, 0, 0, c, s, 0, -s, c); Vector3D u = m * v; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 97 this • In una classe è automaticamente definito un attributo particolare: this – this è un puntatore all’oggetto di cui fa parte – E’ particolarmente utile quando si definisce un operatore di assegnazione (=): Vector2D.h Vector2D.cc Vector2D& operator=(const Vector2D& v){ class Vector2D { x_=v.x(); public: y_=v.y(); Vector2D& operator=(const Vector2D& ); return *this; // ... } private: main.cc double x_, y_; }; #include “Vector2D.h” L’operatore = ritorna una int main() { Vector2D null(0, 0); referenza a se stesso. Vector2D a, b; Permette assegnazioni a=b=null; } multiple Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 98 Overloading di operatori • possono esistere funzioni con lo stesso nome ma con argomenti diversi Vector2D.h class Vector2D { public: // ... private: double x_, y_; }; Vector2D operator*(const Vector2D &, double); double operator*(const Vector2D&, const Vector2D&); Vector2D.cc Vector2D operator*(const Vector2D&, double s) { return Vector2D( v.x() * s, v.y() * s); } double operator*(const Vector2D& v1, const Vector2D& v2) { return ( v1.x() * v2.x() + v1.y() * v2.y() ); } • Non bisogna pero` esagerare! Ogni operatore deve avere un significato ben preciso, per ragioni di chiarezza. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 99 Overloading di operatori (2) • Permette di utilizzare tipi definiti dall’utente come se fossero tipi fondamentali • La cardinalita`, l’associativita` e la precedenza di un operatore non possono essere modificati • Operatori unari sono implementati come metodi senza argomenti (l’oggetto è l’argomento implicito) • Operatori binari possono essere implementati come metodi con un argomento (il primo argomento, implicito, è l’oggetto il cui operatore agisce) o come funzioni friend a due argomenti. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 100 Programmazione generica • Il C++ fornisce un metodo per creare un polimorfismo parametrico. E’ possibile utilizzare lo stesso codice per tipi differenti: il tipo della variabile diventa un parametro template<class T> T max( T p1, T p2 ) { if ( p1 < p2 ) return p2; else return p1; } Per il tipo T deve essere definito l’operatore < Introduzione al C++ e alla programmazione ad oggetti Main.cc int main() { Vector v1,v2; cout << max<int>(10,20) << endl; cout << max<float>(2.6,1.0) << endl; cout << max<Vector>(v1,v2) << endl;} 19-23 febbraio 2001 101 Sintassi template < class identifier > function definition typename template < class identifier > class definition • Ogni volta che nella definizione della funzione o della classe appare identifier questo viene sostituito dal compilatore con il tipo fornito nella chiamata. • La dichiarazione e l’implementazione del template devono essere nello stesso file ove il template viene utilizzato Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 102 Parametri templati • Parametri interi possono essere inclusi nella dichiarazione del template template <typename T=int , int n=10> class array_n { ... private: T items[n]; esplicitamente // n istanziato }; array_n<complex, 1000> w; di complessi // w array • I parametri di default possono essere tralasciati Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 103 Templates di templates • L’argomento di un template puo` essere esso stesso un template template <class T1, template <class T2> class T3 > • questo permette la creazione e l’utilizzo di metatemplates (templates istanziati con templates) molto sofisticati • la Standard Template Library fa uso di questa possibilita` Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 104 Funzioni template e parametri • Una buona parte dei compilatori accetta una sintassi ristretta per quel che riguarda le funzioni template. ANSI/C++ prevede invece che anche parametri numerici possano essere inclusi nella definizione del template template <class T> void swap(T& x, T& y){ T temp; temp=x; x=y; y=temp; } OK per ogni compilatore template <class T, int n=10> T aFunc(){ T temp[n]; . . . } ANSI/C++, ma la maggior parte dei compilatori lo rifiuta Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 105 Membri statici • Per le classi template, gli attributi statici non sono universali ma specifici di ogni istanza template <class T> class MyClass { public: static int counter; ... }; MyClass<int> a,b; MyClass<double> c; • Le variabili statiche MyClass<int>::counter e MyClass<double>::counter sono diverse Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 106 Un esempio: lo stack di interi Contenuto val Stack top ... next Stack top Lo stack vuoto Contenuto Contenuto val val next next class Contenuto { ... private: Contenuto* next; int val; }; class Stack { ... private: Contenuto* top; }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 107 Un esempio: lo stack di interi class Stack { public: Stack() {top = 0;} ~Stack() {} void push ( int i ) { Contenuto* tmp = new Contenuto(i,top ); top = tmp; } int pop () { int ret = top->getVal(); Contenuto* tmp = top; top = top->getNext(); delete tmp; return ret; } private: Contenuto* top; }; class Contenuto { public: Contenuto ( int i, Contenuto* ptn ) { val=i; next=ptn; } int getVal (){ return val; } Contenuto* getNext() {return next;} private: Contenuto* next; int val; }; User code int main() { Stack s; s.push ( 10 ); s.push ( 20 ); cout << s.pop() << “ - “ << s.pop; return 0; }; Output >> 10 - 20 Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 108 Lo stack “templato” template <class T> class Stack { public: Stack() {top = NULL;} ~Stack() {;} void push ( T i ) { Contenuto<T>* tmp = new Contenuto<T> (i,top ); top = tmp; } T pop () { T ret = top->getVal(); Contenuto<T>* tmp = top; top = top->getNext(); delete tmp; return ret; } private: Contenuto<T>* top; }; Introduzione al C++ e alla programmazione ad oggetti template <class T> class Contenuto { public: Contenuto ( T i, Contenuto* ptn ) { val = i; next = ptn; } T getVal (){ return val; } Contenuto* getNext() {return next;} private: Contenuto* next; T val; }; User code int main() { Stack<int> s; s.push ( 10 ); s.push ( 20 ); Stack<double> s1; Stack<Shape *> s2; cout << s.pop() << “ “ << s.pop; return 0;}; 19-23 febbraio 2001 109 La Standard Template Library • La libreria standard STL e’ una libreria di classi di contenitori, algoritmi ed iteratori. • STL e’ una libreria generica: tutti i suoi componenti sono parametrizzati mediante l’utilizzo dei template puntatori intelligenti vettori, liste, mappe, …. find, replace, reverse, sort, …. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 110 Iteratori (puntatori intelligenti) • Gli iteratori sono dei puntatori agli elementi di un contenitore e ci permettono di muoverci all’interno di esso: – Iteratori monodirezionali: Permettono di accedere all’elemento successivo o al precedente – Iteratori bidirezionali : Permettono di accedere sia all’elemento successivo che al precedente – Iteratori ad accesso casuale : Permettono di accedere ad un qualunque elemento del contenitore Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 111 Contenitori • Un contenitore è un oggetto capace di immagazzinare altri oggetti e che possiede metodi per accedere ai suoi elementi. – Ogni contenitore ha un iteratore associato che permette di muoversi tra gli elementi contenuti – Una sequenza è un contenitore di lunghezza variabile i cui elementi sono organizzati linearmente. E’ possibile aggiungere e rimuovere elementi – Un contenitore associativo è una sequenza che permette un efficiente accesso ai suoi elementi basato su una chiave. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 112 Sequenze • vector – Tempo costante di inserimento e cancellazione di elementi all’inizio e alla fine del vettore. – Tempo lineare con il numero di elementi per inserimento e cancellazione di elementi all’interno del vettore – Iteratore ad accesso casuale • list – Tempo costante di inserimento e cancellazione di elementi in ogni punto della lista – Iteratore bidirezionale Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 113 vector begin() end() 0 1 2 ... 9 ++ p p p p p push_back() p • Le locazioni di memoria sono contigue – Accesso casuale, veloce l’accesso agli elementi, lenti inserimento ed estrazione Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 114 list nodo val list ... next top bottom prev nodo nodo val val next next prev prev • Simile allo stack, ma consente di muoversi in due direzioni • Le locazioni di memoria non sono contigue – Lenta la ricerca, veloci inserimento ed estrazione Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 115 Contenitori associativi • Sono contenitore di coppie ( key, value ) e possiedono un iteratore bidirezionale • map – Viene richiesto l’operatore < per la chiave – Gli elementi sono ordinati secondo la chiave Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 116 Algoritmi • Gli algoritmi sono delle funzioni globali capaci di agire su contenitori differenti sort find fill count copy min, max • Sono incluse operazioni di ordinamento (sort, merge, min, max...), di ricerca (find, count, equal...), di trasformazione (transform, replace, fill, rotate, shuffle...), e generiche operazioni numeriche (accumulate, adjacent difference...). Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 117 Esempio uso sequenze #include <vector list > #include <algorithm> #include <iostream> int main() { vector list <int> container; int val; for (int i=0; i<10; i++) { val = (int)((float)rand()/RAND_MAX*10); container.push_back(val); } vector list <int>::iterator it1; for ( it1=container.begin(); it1!=container.end(); it1++) cout << "vector : " << *it1 << endl; return 0; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 118 Esempio uso contenitori associativi #include <map> #include <algorithm> #include <iostream> #include <string> int main() { map<string,int> amap; amap["Primo”]=1; amap[“Secondo”]=2; cout << "Size : " << amap.size() << endl; amap["Terzo"]=3; amap["Quarto"]=4; cout << "Size : " << amap.size() << endl; map<string,int>::iterator it; for ( it=amap.begin(); it!=amap.end(); it++) cout << "map : " << it->first << " " << it->second << endl; cout << amap.find("Terzo")->second << endl; return 0; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 119 Assegnazione di un metodo ad un messaggio • I metodi pubblici di una classe costituiscono l’interfaccia della classe (cioè i messaggi che l’oggetto può interpretare) • La funzione è assegnata al messaggio in fase di codifica (early binding) • Può essere necessario assegnare la funzione al messaggio a run-time (late binding) Polimorfismo Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 120 Controllo dei tipi • Controllare i tipi significa verificare che ad un oggetto vengano inviati solo messaggi che è in grado di comprendere: – controllo del nome del metodo – controllo della lista degli argomenti • In C++ il controllo è fatto dal compilatore (strong typing) • In altri linguaggi (ad esempio SmallTalk) è fatto a run-time (weak typing) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 121 Typing & Binding Consistenza dei Definizione Strong tipi verificata Typing dei messaggi dal compilatore Consistenza dei e degli Weak tipi verificata argomenti a run-time Assegnazione Binding di un metodo ad un messaggio Introduzione al C++ e alla programmazione ad oggetti In fase di Early programmazione INFLESSIBILE A run-time Late POLIMORFISMO 19-23 febbraio 2001 122 Esempio: i soldati • Tutti i soldati devono capire il messaggio attacca. Il messaggio ha conseguenze diverse a seconda del tipo di soldato: – un arcere lancia una freccia – un fante usa la spada – un cavaliere lancia una lancia • Il gestore della schermata vuole tenere una lista di soldati e vuole poter dire ad ogni soldato di attaccare indipendentemente dal tipo ma basandosi solo sulla posizione. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 123 list<Soldato> lista; riempiLista(lista); Posizione unaPosizione=...; list<Soldato>::iterator iter; for(iter=lista.begin();iter!=lista.end();iter++){ Soldato unSoldato=(*iter); if(unSoldato.posizione()==unaPosizione) unSoldato.attacca(); } class Soldato { void attacca() { // cosa scrivo qui?!? Per quale tipo di // soldato implemento il metodo attacca()? } }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 124 Polimorfismo • Polimorfismo con tipi controllati dal compilatore (Strong typing & late binding). Come? • In C++ viene implementato tramite il concetto di ereditarietà (inheritance) • Classe astratta: definisce i messaggi • Classe concreta: assegna i metodi ai messaggi La classe concreta eredita da quella astratta Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 125 Ereditarietà • Una classe può essere derivata da una classe esistente usando la sintassi: class newclass: (public|protected|private) oldclass { dichiarazioni... }; – public, protected e private specificano il tipo di accesso ai membri della classe • Se la classe base non ha un costruttore di default: – La classe derivata deve implementarlo • Se la classe base ha un costruttore di default: – il costruttore della classe derivata deve esplicitamente invocarlo nella sua lista di inizializzatione • Il costruttore della classe base può così essere eseguito prima che il costruttore della classe derivata sia eseguito Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 126 Ereditarietà (2) • Una classe derivata pubblicamente è a tutti gli effetti un sottotipo della classe base. – Un oggetto della classe derivata può essere trattato come se fosse un oggetto della classe base – Un puntatore alla classe base può puntare ad oggetti della classe derivata – Un riferimento alla classe derivata può, se la cosa ha un senso, essere implicitamente convertito ad un riferimento alla classe base – E` possibile dichiarare un riferimento alla classe base ed inizializzarlo ad un oggetto della classe derivata Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 127 Ereditarietà (3) • La definizione dell’interfaccia (metodi pubblici) della classe base è estremamente importante perchè determina il comportamento delle classi derivate • Un metodo della classe base può essere: – dichiarato e definito normalmente • la classe derivata eredita questo metodo e NON può ridefinirlo – dichiarato virtual e definito normalmente • la classe derivata eredita questo metodo e può ridefinirlo – dichiarato virtual e non definito (=0) • la classe derivata eredita il metodo e DEVE ridefinirlo Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 128 Classi base astratte • Una funzione puramente virtuale è un metodo virtuale non definito. E` dichiarato come: virtual func_prototype = 0; • Una classe che ha almeno un metodo puramente virtuale è chiamata classe astratta • Oggetti di una classe astratta non possono esistere • Puntatori ad una classe base astratta possono essere definiti ed usati polimorficamente (per puntare ad oggetti delle classi derivate) • Una classe base astratta viene introdotta per specificare l’interfaccia di una categoria di classi Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 129 class Soldato { virtual void attacca()=0; }; class Arcere : public Soldato { virtual void attacca() { // lancia una freccia } }; class Fante : public Soldato { virtual void attacca() { // usa la spada } }; ... Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 130 Erediarietà multipla • L’ereditarietà multipla permette di derivare una classe da due o più classi base. La sintassi viene estesa per permettere una lista di classi base class . . . }; class . . . }; A { . B { . class AplusB: public A, private B { . . . . }; • L’ ereditarietà multipla viene spesso utilizzata per combinare un’interfaccia ed una implementazione, ma è molte volte sintomo di un cattivo disegno Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 131 dynamic_cast • dynamic_cast opera una conversione, se è possibile, fra due tipi. Il puntatore ritornato NON è nullo soltanto se il tipo dell’oggetto su cui si opera è quello che ci si aspetta class . . . }; class . . . Base { . // base implementation Derived: public Base { . void new_method() ; // non e’ definito in Base! }; void func(Base *ptr) // ptr e’ un obbetto dell classe Base { ptr->new_method(); // Errore!!! Derived *p = dynamic_cast<Derived *> (ptr) if (p !=0) { p->new_method(); } } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 132 Ereditarietà (4) • Una classe derivata estende la classe base e ne eredita tutti i metodi e gli attributi Track.h class Track { public: LorentzVector momentum() { return p_; } protected: LorentzVector p_; }; DchTrack è una Track che ha degli attributi in più (hits_) e nuovi metodi (DchHit* hit(int n), int hits()) Introduzione al C++ e alla programmazione ad oggetti DchTrack.h #include “Track.h” class DchTrack : public Track { public: int hits() { return hits_->size(); } DchHit* hit(int n) { return hits_[n]; } protected: list<DchHit> hits_; }; 19-23 febbraio 2001 133 Esempio: shape • Tutti gli oggetti nella finestra hanno comportamenti comuni che possono essere considerati in astratto: – – – – disegna sposta ingrandisc etc... Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 134 Cerchi e quadrati Quadrato Introduzione al C++ e alla programmazione ad oggetti Cerchio 19-23 febbraio 2001 135 Cerchio Nome della classe Circle.h Costruttore class Circle { public: Circle(Point2d center, double radius); ~Circle(); void moveAt(const Point2d & p); void moveBy(const Point2d & p); void scale(double s); void rotate(double phi); void draw() const; void cancel() const; private: Point2d center_; double radius_; }; Distruttore Point2d: classe che rappresenta un punto in 2 dimensioni. Interfaccia Pubblica Metodi: operazioni sugli oggetti “Dati” privati (Attributi, membri) Punto e virgola! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 136 Circle.cc #include “Circle.h” Cerchio (2) void Circle::draw() const { const int numberOfPoints = 100; float x[numberOfPoints], y[numberOfPoints]; float phi = 0, deltaPhi = 2*M_PI/100; for ( int i = 0; i < numberOfPoints; ++i ) { x[i] = center_.x() + radius_ * cos( phi ); y[i] = center_.y() + radius_ * sin( phi ); phi += dphi; } polyline_draw(x, y, numberOfPoints, color_, FILL); } void Circle::moveAt( const Point2d& p ) { cancel(); center_ = p; draw(); } void Circle::scale( double s ) { cancel(); radius_ *= s; draw(); } Circle::Circle( Point2d c, double r ) : center_( c ), radius_( r ) { draw(); } Main.cc #include “Circle.h” int main() { Circle c( Point2d(10, 10), 5 ); c.draw(); c.moveAt(Point2d(20, 30)); return 0; } Circle::~Circle() { cancel(); } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 137 QuadratoSquare.cc #include “Square.h” Square.h class Square { public: Square(const Point2d&, const Point2d&, Color color = TRASPARENT); ~Square(); void moveAt( const Point2d& p ); void moveBy( const Point2d& p ); void changeColor( Color color ); void scale( double s ); void rotate( double phi ); void draw() const; void cancel() const; private: Point2d center_; Vector2d centerToUpperCorner_; Color color_; }; centerToUpperCorner_ upperCorner loweCorner Introduzione al C++ e alla programmazione ad oggetti void Square::draw() const { float x[4], y[4]; Vector2d delta( centerToUpperCorner_ ); for ( int i = 0; i < 4; i++ ) { Point2d corner = center_ + delta; x[i] = corner.x(); y[i] = corner.y(); delta.rotate( M_PI_2 ); } polyline_draw(x, y, 4, color_, FILL); } void Square::rotate( double phi ) { cancel(); centerToUpperCorner_.rotate( phi ); draw(); } Square::Square(const Point2d& lowerCorner, const Point2d& upperCorner, Color color) : center_( median(lowerCorner, upperCorner) ), centerToUpperCorner_( upperCorner - center_ ), color_( color ) { draw(); } void Square::scale( double s ) { cancel(); centerToUpperCorner_ *= s; 19-23 febbraio 2001 draw(); } 138 Codice Applicativo (Client) Main.cc #include “Circle.h” #include “Square.h” int main() { Circle c1( Point2d(2.,3.), 4.23 ); Square r1( Point2d(2.,1.), Point2d(4.,3.) ); Costruisce un vettore di puntatori a e salva Circle * circles[ 10 ]; cerchi, crea oggetti in memoria for ( int i = 0; i < 10; ++i ) i loro puntatori nel vettore. { circles[ i ] = new Circle( Point2d(i,i), 2. ); } for ( int i = 0; i < 10; ++i ) circles[ i ]->draw(); return 0; } Itera sul vettore e invoca draw() per ogni elemento Come gestire cerchi e quadrati insieme? Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 139 Polimorfismo Tutte le Shapes hanno la stessa interfaccia: draw, pick, move, fillColor..., ma ogni sottotipo diverso può avere la usa personale implementazione Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 140 Interfaccia astratta Shape.h class Shape { public: Shape() { } virtual ~Shape() { } Main.cc #include “Circle.h” moveAt(const Point2d& where) = 0;#include “Square.h” changeColor(Color newColor) = 0; scale(double s) = 0; int main() rotate(double phi) = 0; { draw() const = 0; Shape * shapes[ 20 ]; cancel() const = 0; int index = 0; for ( int i = 0; i < 10; i++ ) { Shape * s; s = new Circle( Point2d(i, i), 2.) ); shapes[ index ++ ] = s; s = new Square( Point2d(i, i), Point2d(i+1, i+2)) ); shapes[ index ++ ] = s; } #include “Shape.h” for ( int i = 0; i < 20; i++ ) class Square : public Shape shapes[ i ]->draw(); { // …. Il resto tutto uguale a prima return 0; }; } virtual virtual virtual virtual virtual virtual }; void void void void void void Interfaccia di metodi puramente virtuali Square.h Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 141 Ereditarietà e riuso del codice CenteredShape.h Class CenteredShape: public Shape { public: CenteredShape(Point2d c, Color color = TRASPARENT) : center_(c), color_(color) { /*draw();*/ } ~Circle() { /*cancel();*/ } Non si possono chiamare metodi virtuali in costruttori e distruttori (troppo presto, troppo tardi) Square.h #include “CenteredShape.hh” void moveAt( const Point2d& ); void moveBy( const Vector2d& ); class Square : public CenteredShape void changeColor( Color ); { virtual void scale( double ) = 0; public: virtual void rotate( double ) = 0; Square( Point2d lowerCorner, Point2d upperCorner, virtual void draw() const = 0; Color col = TRASPARENT) : virtual void cancel() const = 0; CenteredShape( median(lowerCorner, upperCorner), col), touc_(upperCorner - center_) { draw(); } protected: ~Square() { cancel(); } Point2d center_; Color color_; virtual void scale( double s ) }; { cancel(); centerToUpperCorner_ *= s; draw(); } virtual void rotate( double phi ); virtual void draw() const; virtual void cancel() const; private: Vector2d touc_; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 }; 142 Attenzione alle generalizzazioni... Rectangle.h • Attenzione: scegliere le relazioni di ereditarietà può essere non banale. • Un quadrato è un rettangolo? class Rectangle { public: Rectangle(double x0, double y0, double lx, double ly) : lx_(lx), ly_(ly), x0_(x0), y0_(y0) { } void scaleX(double s); void scaleY(double s); protected: double x0_, y0_; Square.h double lx_, ly_; }; class Square : public Rectangle { public: Square(double x0, double y0, double l) : Rectangle(x0, y0, l, l) { } }; Avere lx_ e ly_ è ridondante per Square Cosa succede se si invoca scaleX o scaleY ? Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 143 Ereditarietà multipla • Una classe può ereditare da più classi DrawableObj.h Shape.h class DrawableObj { public: virtual void draw() = 0; }; class Shape { public: virtual void scale(double s) = 0; virtual void moveAt( Vector2d& ) = 0; }; DrawableShape.h class DrawableShape : public DrawableObj, public Shape { public: virtual void draw(); virtual void scale(double s); virtual void moveAt( Vector2d& ); }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 144 Strategie di sviluppo di un progetto • • • • • • Requisiti: cosa l’utente vuole Analisi: la visione dell’informatico dei requisiti Disegno: l’aspetto del sistema software Produzione: codifica Testing: debugging e verifica dei requisiti Mantenimento: installazione del prodotto e controllo del funzionamento per il resto della sua vita Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 145 Modello a cascata Requisiti Analisi Disegno Produzione Testing Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 146 Modello evoluzionario Requisiti Analisi Testing Disegno Produzione Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 147 Confronto fra i modelli di sviluppo A cascata Evoluzionario • Processo lineare (si torna al passo precedente solo in caso di problemi) • Confinamento delle attività in ogni fase • Facile da gestire (gestione delle scadenze) • Difficile da modificare • Prodotto utilizzabile solo alla fine del processo • Processo ciclico (brevi processi completi) • Attività distribuite su più fasi • Difficile da gestire • Facile da modificare e integrare • Prototipo utilizzabile fin dal primo ciclo Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 148 Requisiti • Definizione delle richieste da parte dell’utente del programma (o di una sua parte) sul sistema • Si parla di programmazione per contratto perchè l’utente richiede solamente la definizione del servizio richiesto NON la metodologia seguita per fornirglielo – è possibile delegare parte del lavoro richiesto ad altri – il sistema è indipendente da chi è il suo utente INCAPSULAMENTO! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 149 Analisi • Comprensione e razionalizzazione delle richieste dell’utente • Costruzione di un modello – astrazione (semplificazione delle relazioni) – rilevanza (identificazione degli oggetti chiave) • Da non trascurare: analisi delle soluzioni esistenti. Può far risparmiare molto tempo!!! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 150 Disegno Definizione di oggetti e classi Definizione delle interfacce Definizione degli stati e dell’implementazione Definizione delle relazioni Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 151 Disegno (2) • Dopo ogni ciclo bisogna analizzare i rischi, la stabilità del disegno e la complessità delle classi • Se una classe è troppo complessa conviene dividerla • Ad ogni ciclo il numero di modifiche deve diminuire • Architetture troppo complesse devono essere modularizzate Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 152 Codifica • C’è poco da dire… • Non sopravvalutate questa fase: Suddivisione del tempo per il primo ciclo Testing Analisi 20% 30% Codifica 15% Disegno 35% Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 153 Testing • Debugging: è ovvio… il codice non deve dare errori. • Use cases: specificano il comportamento del sistema in una regione. • Scenarios: sono esempi concreti di use cases. Per definizione se tutti gli scenari sono soddisfatti correttamente il test è positivo. Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 154 Metodi di sviluppo del software Un metodo comprende: • Una notazione mezzo comune per esprimere strategie e decisioni • Un processo specifica come deve avvenire lo sviluppo Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 155 Metodi Object Oriented – Booch Method by Grady Booch – OMT Grady Booch by Jim Rumbaugh – Objectory (Use Cases) by Ivar Jacobson – CRC by R.Wirfs-Brock Jim Rumbaugh • Di recente introduzione: UML – uno standard OMG (Object Management Group), dal novembre 1997 Ivar Jacobson Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 156 UML per l’analisi e il disegno • Class Diagrams: aspetto statico del sistema. Classi con attributi e metodi e relazioni tra di esse. • Sequence e collaboration digrams: comportamento dinamico del sistema. Sequenza dei messaggi scambiati fra gli oggetti. • Use case diagrams: illustra gli use cases, le relazioni fra di essi e gli attori che vi partecipano. • State diagrams: descrive gli stati in cui ogni oggetto si può trovare e le modalità con cui passa da uno stato all’altro Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 157 Concetti delle classi rivisitati • Relazioni tra oggetti • Decomposizione funzionale all’interno di una classe – responsabilità dei metodi • Decomposizione funzionale tra più classi – responsabilità delle classi Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 158 Rappresentazione delle classi Nome - dato - dato attibuti pubblico protetto + metodo(arg) # metodo(arg) - metodo(arg) operatori privato Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 159 Rappresentazione di una classe C++ in UML Nome.h class Nome { private: Tipo1 variabile1; Tipo2 variabile2; Tipo3 variabile3; public: Nome(); ~Nome(); Tipo4 funzione1 ( arg ); protected: Tipo5 funzione2 ( arg ); private: Tipo6 funzione3 ( arg ); }; Introduzione al C++ e alla programmazione ad oggetti Nome - variabile1:Tipo1 - variabile2:Tipo2 - variabile3:Tipo3 + funzione1(arg):Tipo4 # funzione2(arg):Tipo5 - funzione3(arg):Tipo6 19-23 febbraio 2001 160 Attributi e metodi Protetto (#) Publico (+) Notazione di Rational Rose Privato (-) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 161 Principali relazioni fra classi • associazione • aggregazione by reference (il composito non vive senza il componente) • aggregazione by value (aggregazione fisica: esistenza contemporanea) • dipendenza • generalizzazione (inheritance) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 162 Aggregazione (contenimento) Autista By reference (condivisa) • un autista guida più automobili Automobile Motore Introduzione al C++ e alla programmazione ad oggetti By value (possesso) • una automobile possiede il suo motore 19-23 febbraio 2001 163 Cardinalità e direzionalità Non navigabile -_points Polygone 1 Point 1..* •Il punto non conosce i poligoni •Il poligono è costituito da punti Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 164 Dipendenza • Non c’è nessuna associazione • C’è comunque relazione di uso •Il CD non conosce il CDPlayer •Il CDPlayer usa il CD: se cambia il formato del CD il CDPlayer deve essere modificato Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 165 Generalizzazione (ereditarietà) Autista #_autista Automobile #_motore Motore {virtual} {virtual} AutoSportiva AutoDiLusso Ereditarietà virtuale! Ferrari Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 166 Class Diagram di “Shape” Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 167 Class Diagram Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 168 Class Diagram Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 169 Object Sequence Diagram Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 170 Object Collaboration Diagram Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 171 CRC Classi, Responsabilità, Collaborazioni B A z s x y C p F f Introduzione al C++ e alla programmazione ad oggetti D E q w 19-23 febbraio 2001 172 Assegnare Responsabilità • Identificare i protagonisti • Analizzare il ruolo dei vari oggetti • Concentrarsi sul comportamento non la rappresentazione • Cercare Oggetti con proprietà comuni: – appartiene a classi diverse, o sono solo oggetti diversi? • Definire le interfacce (le operazioni che soddisfano le responsabilità) Una corretta assegnazione delle responsabilità è la chiave di una buona modularità e riuso Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 173 Collaborazione tra classi • Le responsabilità vanno suddivise tra i vari oggetti del sistema • non deve esistere un controllo centralizzato • Un oggetto deve compiere le proprie responsabilità e delegare ad altri operazioni specifiche – Legge di Demeter: non usate oggetti lontani: Invece di: traiettoria.listapunti().aggiungi(Punto); usare: traiettoria.aggiungiPunto(Punto); Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 174 Identificare Relazioni • Cercare collaborazioni • Cercare aggregazioni • Cercare generalizazioni Come un client conosce il suo service provider? Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 175 Relazioni Logiche • Generalizazione: Is-a • Aggregazione: Has • Dipendenza: Knows Introduzione al C++ e alla programmazione ad oggetti { Implementazione • Inheritance • Template instantiation • Composizione by value • Composizione by reference 19-23 febbraio 2001 176 Avere o essere? • Uno dei punti critici è distinguere se il rapporto fra due oggetti è del tipo avere o essere: – Un LorentzVector è un Vector o ha un Vector? – Una Traccia è un vector<Hit> o ha un vector<Hit>? – Un Rivelatore è una Superficie o ha una superficie? • Per risolvere il problema bisogna guardare a cosa fanno! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 177 Principio di Liskov • Gli oggetti figli possono essere usati ovunque l’oggetto genitore è richiesto – usare l’inheritance quando è richiesto il polimorfismo – Non cambiare il comportamento della base class Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 178 Composizione by value o by refrence • In C++ la scelta fra aggregazione by value o by refrence può seguire questo schema: – – – – Tipi semplici (int, float, …): by value Parte dello stato dell’oggetto: by value Oggetti condivisi: by reference Assegnati a run time: by reference • Oggetti condivisi by reference: attenzione a chi ha la responsabilità di crearli e cancellarli! (1 new 1 delete!) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 179 Approccio Outside-in • Il corretto approccio è quello di guardare il sistema dall’esterno. • Identificare prima di tutto gli oggetti che interagiscono con l’utente esterno e i messaggi a cui devono saper rispondere (think client!) • In seguito identificare gli oggetti che forniscono servizi a questi ultimi e così via • Gli algoritmi vengono per ultimi!!! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 180 CRC Workshop • Metodo per la definizione si una architettura bilanciata • Ogni partecipante svolge il ruolo di una classe. – – – – Individuazione delle classi Contrattazione delle responsabilità Definizione delle collaborazioni Difesa dal tentativo di assegnazione di responsabilità contrarie alla natura della classe Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 181 Regole per il CRC workshop • Tentate di rifuutare le responsabilità – Dovrei? (Non sono io che lo devo fare!) – Potrei? (Non ho i mezzi, o lo stato per farlo!) • Cercate di fare poco lavoro – Se avete dovuto accettare una responsabilità cercate di far fare il lavoro a qualcun’altro • Potenziate i collaboratori, non interferite Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 182 Design Patterns • Sono elementi di software OO riutilizzabile • Piccoli insiemi di classi che collaborano implementando dei comportamenti tipici – Creational patterns – Structural patterns – Behavioral patterns • I principali sono raccolti in un libro: E. Gamma et al., Design Patterns Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 183 Factory I client possono richiedere la creazione di un prodotto senza dipendervi Client Factory AbstractProduct createProduct1 () : AbstractProduct createProduct2 () : AbstractProduct ConcreteProduct1 Introduzione al C++ e alla programmazione ad oggetti La Factory dipende dai prodotti concreti, mentre i client dipendono solo da quelli astratti ConcreteProduct2 19-23 febbraio 2001 184 Proxy Subject Client reques t( ) Proxy RealSubject _sub ject _s ubject : RealSubject reques t( ) reques t( ) 1 Una richiesta da un client a un server, può essere mediata dal Proxy, che può compiere anche altre operazioni (I/O, caching, etc.) Introduzione al C++ e alla programmazione ad oggetti ... _s ubject->reques t(); ... 19-23 febbraio 2001 185 Composite Client Com ponent Il client può trattare componenti e compositi usando la stessa interfaccia. La composizione può essere ricursiva. operation( ) 1..* _children Com pos ite operation( ) for c in all _children c->operation(); Introduzione al C++ e alla programmazione ad oggetti Leaf operation( ) Esempio: programmi di grafica 19-23 febbraio 2001 186 Gruppo di Shapes Client Shape draw( ) _components 1..* Il gruppo di shapes è il Composite La shape è il Component Le shapes concrete (Circle, Square, ecc...) sono le Leaf GroupofShapes draw( ) Introduzione al C++ e alla programmazione ad oggetti Circle, Square, ... draw( ) 19-23 febbraio 2001 187 Codice del modello composite Shape.h class Shape { public: Shape() {} virtual void draw() const = 0; // altri metodi virtuali ( = 0 ) }; Circle.h #include “Shape.h” class Circle: public Shape { public: Circle(Point2D c, double r): Shape(), center_(c), radius_(r) {} void draw() const { ; // draw circle } // altri metodi definiti per Circle private: double radius_; Point2D center_; }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 188 Codice del modello composite GroupofShapes.h #include “Shape.h” class GroupofShapes : public Shape { public: typedef vector<Shape *> Container; typedef Container::const_iterator Iterator; GroupofShapes(){} void draw() const { Iterator p=components.begin(); Iterator pe=components.end(); while (p!=pe) { (*p)->draw(); p++; } return; } // gli altri metodi sono definiti operando // sui componenti protected: Container components; }; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 189 Strategy { ... Strategy* s ; s ->doAlgorithm(); ... } Il pattern Strategy permette di scegliere l’algoritmo da eseguire a runtime. Nuovi algoritmi possono essere introdotti senza modificare il codice utente. Strategy Client doAlgorithm( ) ConcreteStrategyA doAlgorithm( ) Introduzione al C++ e alla programmazione ad oggetti ConcreteStrategyB ConcreteStrategyC doAlgorithm( ) 19-23 febbraio 2001 doAlgorithm( ) 190 Observer Observer Subject _ob servers update( ) _observers : Observer attach (Observer) notify () 0..* for all o in _ observables o->update(); ConcreteObserver ConcreteSubject _status : Status _subject . ConcreteSubject _sub ject _status : Status status( ) return _status; Lo stato dell’Observer dipende dallo stato del Subject. Il Subject notifica a tutti gli Observer registrati che il suo stato è cambiato. Introduzione al C++ e alla programmazione ad oggetti update( ) _status = _subject->status(); 19-23 febbraio 2001 191 Appendice: strighe C-style • Le variabili carattere sono gestite come array di char (un char contiene un solo carattere) – accesso agli elementi tramite la sintassi degli array – carattere nullo usato come terminatore (‘\0’) • Funzoni di libreria per la gestione dei char* : – #include<cstring> per utilizzarle – int strlen(const char*); lunghezza della stringa – int strcmp(const char*, const char*); confronto di due stringhe – char* strcpy(char*, const char*); copia la seconda stringa nella prima Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 192 Appendice: la classe string • Per semplificare la gestione delle stringhe è stata creata la classe string – #include<string> per usarla – Definiti gli operatori standard: • = per l’assegnazione • + e += per la concatenazione • == e tutti gli altri operatori relazionali per il confronto • [] per l’accesso agli elementi – Disponibile sintassi simile a quella dei contenitori STL: • iteratori: string::iterator e string::const_iterator • funzioni begin() , end() , size() , ecc... – Interoperabilità con char*: char* c=“Pippo”; string s=c; char* c1 = s.c_str(); s += c; Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 193 Confronto stringhe C-style e string #include<iostream> #include<iostream> #include<cstring> int main(){ int err=0;int big=1000000; char* c1=“LLLong string”; for(int i=0;i<big;i++){ int len=strlen(c1); char* c2=new char[len+1]; strcp(c2,c1); if(strcmp(c2,c1))err++; delete[] c2; } cout<<err<<“errori”<<endl; return 0; } Introduzione al C++ e alla programmazione ad oggetti #include<string> int main(){ int err=0;int big=1000000; string s1=“LLLong string”; for(int i=0;i<big;i++){ // int len=s1.size(); string s2=s1; if(s2!=s1)err++; } cout<<err<<“errori”<<endl; return 0; } // 2 volte piu’ veloce!!! 19-23 febbraio 2001 194 Appendice:operazioni di I/O • Si utilizza la libreria iostream – Gli operatori di stream >> e << dirigono il flusso da/per le unità desiderate: • cout : standard output. Si sono già visti molti esempi • cerr : standard error. Si usa come cout • cin : standard input (normalmente la tastiera) include<iostream> include<string> int main(){ string nome; cout << “Come ti chiami?” << endl; cin >> nome; // Notare la direzione!!! if(nome.empty()) cerr << “Stringa nulla!” << endl; else cout << “Ciao “ << nome << “!” << endl; return 0; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 195 Overloading degli operatori di I/O • Gli operatori << e >> possono essere ridefiniti per consentire operazioni del tipo: Vector2D v(1,2); cout << “Il vettore v vale “ << v << endl; • Si utilizza una funzione friend: class Vector2D { friend ostream& operator <<(ostream& os, const Vector2D v); [...] } ostream& operator <<(ostream& os, const Vector2D v){ os << “(“ << v.x() << “,” << v.y() << “)”;} • Si ottiene: Il vettore v vale (1,2) Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 196 Appendice: I/O con files • E’ possibile definire altre unità di I/O – – – – – Si utilizza la libreria fstream (include iostream) I files di input sono dichiarati ifstream I files di output sono dichiarati ofstream I files di input/output sono dichiarati fstream Costruttore con argomento const char* (nome file) #include <fstream> #include <string> int main(){ ifstream fin(“file1.dat”); // deve esistere! if(!fin){ cerr << “file1.dat non esiste” << endl; return -1; } ofstream fout(“file2.dat”); // se esiste viene sovrascritto int i=0; string parola; while (inf >> parola) fout << “La “ << ++i << “-esima parola e\’ “ << parola << endl; fin.close(); fout.close(); return 0; } Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 197 Appendice: I/O in memoria • E’ possibile definire unità di I/O in memoria (non legate a files) – – – – Si utilizza la libreria sstream (include iostream) Le unità di input sono dichiarati istringstream Le unità di output sono dichiarati ostringstream Le unità di input/output sono dichiarati stringstream – I costruttori non hanno argomento – Il metodo str() applicato ad un oggetto di questo tipo ritorna la stringa (string) contenuta nell’unità: ostringstream messaggio; messaggio << “Ciao!” << endl; string s=messaggio.str(); Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 198 Appendice: Manipolatori di I/O • Modificano il comportamento di una stream. boolalpha: true e false rappresentati come stringhe noboolalpha: true e false rappresentati come 1 e 0 (default) showbase: interi stampati col prefisso che indica la base noshowbase: interi stampati senza il prefisso (default) showpoint: floating point stampati sempre col punto decimale noshowpoint: stampa i floating point come interi se non frazionari (default) showpos: stampa + per numeri positivi noshowpos: non stampa + per i numeri positivi (default) skipws: salta gli spazi bianchi in input (default) noskipws: non salta gli spazi bianchi in input uppercase: stampa 0X in esadecimale, E in scientifica lowercase: stampa 0x oppure e (default) dec: interi in base 10 (default) hex: interi in base 16 oct: interi in base 8 Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 199 Appendice: Manipolatori di I/O (2) left: aggiunge caratteri di riempimento alla destra del val. right: aggiunge caratteri di riempimento alla sinistra internal: aggiunge caratteri fra segno e valore fixed: floating point in notazione decimale (default) scientific: floating point in notazione scientifica flush: svuota il buffer ends: aggiunge il carattere nullo (\0) e svuota il buffer endl: aggiunge un “newline” e svuota il buffer ws: “mangia” gli spazi bianchi • I seguenti manipolatori richiedono: #include <iomanip> setfill(ch): definisce il carattere di riempimento setprecision(n): definisce la precisione per i floating point setw(n): scrive o legge in n caratteri setbase(b): interi in base b Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 200 Esempio di I/O con manipolatori #include <iomanip> int main() { cout << "inserisci un numero: "; double num=0; while(cin >> num) { int pi = (int)(num); cout << setfill('0') << setprecision(5); cout << "Il numero inserito e\' " << num << endl; cout << "La parte intera e\' " << pi << "(" << hex << setw(6) << pi << " esadecimale)" << dec << endl; cout << "La parte frazionaria e\' " << num-pi << endl; cout << "inserisci un numero: "; } inserisci un numero: 12345.678 return 0; Il numero inserito e' 12346 } La parte intera e' 12345(003039 esadecimale) La parte frazionaria e' 0.678 inserisci un numero: Non tutti i compilatori supportano tutti i manipolatori!!! Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 201 Per saperne di più • sugli elementi di base del linguaggio C++ **** Lippman, Lajoye, The C++ Primer, 3rd Edition - Addison Wesley *** Pohl, Object-Oriented Programming Using C++, 2nd Edition - Addison Wesley *** Stroustrup, The C++ Programming Language, 3rd Edition - Addison Wesley • su trucchi e tranelli in C++ **** Myers, Effective C++, Addison Wesley **** Myers, More Effective C++, Addison Wesley *** Coplien, Advanced C++, Addison Wesley • su STL **** Glass, Schuchert, The STL <PRIMER>, Prentice Hall *** Ammeraal, Wiley, STL for C++ Programmers ** Musser, Saini, STL Tutorial and Reference Guide, Addison Wesley Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 202 Per saperne di più (2) • su OO A&D *** Booch, Object-Oriented Analysis and Design with Applications, Benjamin/Cummings *** Booch, Object Solutions, Addison Wesley • su UML **** Fowler, Scott, UML Distilled, Addison Wesley *** Booch, Rumbaugh, Jacobson, The Unified Modeling Language User Guide, Addison Wesley • sui Design Patterns *** Gamma, Helm, Johnson, Vlissides, Design Patterns, Addison Introduzione al C++ e alla programmazione ad oggetti 19-23 febbraio 2001 Wesley 203