C++ - innocentonnice
Transcript
C++ - innocentonnice
Introduzione al C++ Luca Lista, INFN Napoli Luca Lista - C++ Programmazione procedurale • Uno dei principali problemi del software è la sua evoluzione e la sua manutenzione – specialmente in grossi progetti come un esperimento di HEP • Esempi con linguaggi procedurali (Fortran): – Cosa succede quando il codice viene modificato – Dipendenze all’interno del codice Luca Lista - C++ 1 Un esempio semplice • Un esempio “elegante”: copiare una sequenza di caratteri dalla tastiera alla stampante SUBROUTINE COPY LOGICAL READKB CHARACTER C DO WHILE (READKB(C)) CALL WRTPRN(C) ENDDO RETURN • Questo codice dovrà prima o poi essere modificato (aggiornamenti, estensioni, richieste dagli utenti, ecc.) Luca Lista - C++ Modifiche dei requisiti • Una modifica un po’ meno elegante: scrivere anche su un file SUBROUTINE COPY(FLAG) LOGICAL READKB INTEGER FLAG CHARACTER C DO WHILE (READKB(C)) IF (FLAG .EQ. 1) CALL WRTPRN(C) ELSE CALL WRTFL(C) ENDIF ENDDO RETURN Luca Lista - C++ 2 Evoluzione incontrollata • Un’altra modifica per niente elegante: leggere anche de un file SUBROUTINE COPY(FLAG1, FLAG2) LOGICAL READKB, READFL INTEGER FLAG1, FLAG2 LOGICAL CONT CHARACTER C 10 IF (FLAG1 .EQ. 1) THEN CONT = READKB(C) ELSE CONT = READFL(C) ENDIF IF (CONT) THEN IF (FLAG2 .EQ. 1) THEN CALL WRTPRN(C) ELSE CALL WRTFL(C) ENDIF GOTO 10 ENDIF RETURN Luca Lista - C++ Descrizione dei dati • Esempio: cinematica relativistica Idea: Idea: perché una perchénon nonusare usare unafunction? 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))/... COMMON /MYDATA/ P1(4), P2(4), END + 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) Luca Lista - C++ 3 Evoluzione del codice • Se cambia il formato del common block? COMMON /MYDATA/ P1(4), P(4), E(4), P2(4), P3(4), P4(4) THETA(4), PHI(4) + • Bisogna cambiare la funzione (gli argomenti sono diversi) FUNCTION COSTHETA1(THETA1, THETA2, 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), Luca ListaPHI(1), - C++ + PHI(4)) + 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))/... COSTHETA dipende dalla END struttura dei dati P1 e P2 + COMMON /MYDATA/ P1(4), P2(4), P3(4), P4(4) COSTHETA12 = COSTHETA(P1, P2) COSTHETA13 = COSTHETA(P1, P3) COSTHETA14 = COSTHETA(P1, P4) Il codice di analisi dipende dalla struttura del common block MYDATA Luca Lista - C++ 4 C++/Object Oriented • 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 Luca Lista - C++ Classi e oggetti • Definizione di nuovi tipi (oltre a int, float, double) come: – numeri complessi, – vettori, – matrici, . . . • ma anche: – – – – tracce, superfici, elementi di rivelatori, cluster, . . . • Gli oggetti permettono di modellare una problema che rappresenti la realtà Luca Lista - C++ 5 Concetti base dell’OO • • • • • Classi ed oggetti Incapsulamento Relazione di ereditarietà Polimorfismo Programmazione Generica (C++) Luca Lista - C++ Classi e Oggetti • Un esempio di programma “orientato ad oggetti”: un videogioco Luca Lista - C++ 6 Cliente Cliente Soldato Soldato Luca Lista - C++ FORTRAN ORTRAN vs C/C++ • Sintassi F77 e C a confronto 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 Istruzioni separate da “;” int i; long j; float x; double d; Luca Lista - C++ 7 Tipi predefiniti in C++ 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 128 Può essere unsigned [1] Luca Lista - C++ FORTRAN ORTRAN vs C/C++ • Controllo di flusso del programma DO I = 1, 10 . . . ENDDO for ( i = 1; i <= 10; i++ ) { . . . } IF (I.EQ.10 .AND. J.GT.4 .OR. X) THEN . . . ENDIF DO WHILE(X .NE. 5) . . . ENDDO if ( i == 10 && j > 4 || x ) { . . . } while( x != 5 ) { . . . } Luca Lista - C++ 8 Hello, world! • Esempio di programma semplice con I/O #include <iostream.h> int main() { cout << “Hello, world !” << endl; direttiva al preprocessore return 0; } end of line • Come compilare: c++ program.cc -o test Luca Lista - C++ Variabili locali e scope • Le dichiarazioni possono essere fatte ovunque nel codice • Le dichiarazioni di variabili sono valide solo entro un certo“scope” #include <iostream.h> const float pi = 3.1415396; int main() { int j = 0; if ( j == 0 ) { int k = 5; } pi globale j locale j = k; // errore: non compila ! return 0; } void myRoutine( float x ) { int y = x * 2 * pi / 180; cout << “f(x) = “ << y << endl; } Luca Lista - C++ 9 Funzioni matematiche • In C++ non esistono funzioni predefinite #include <iostream.h> #include <math.h> math.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; Niente 2**4. Usare pow(2, 4) } Luca Lista - C++ Array • In C++ sono supportati gli array di dimensione int main() fissa { int x[10]; • Inizializzazione: 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; int x[] = { 1, 2, 3, 4 }; char[] t = { ‘C’, ‘i’, ‘a’, ‘o’, ‘\0’ }; char[] s = “Ciao”; int m[2][2] = { {11, 12}, {21, 22} }; } • L’indice va da 0 a n-1. Usare un indice maggiore di n-1 può causare un crash. Luca Lista - C++ 10 Puntatori • Riferimento ad una locazione di memoria #include <iostream.h> ptr j int main() { int j = 12; int *ptr = &j; cout << *ptr << endl; j = 24; cout << *ptr << endl; cout << ptr << endl; 24 12 return 0; } 12 24 0x7b03a928 indirizzo di memoria Luca Lista - C++ Puntatori • Puntatore nullo #include <iostream.h> ptr j 12 int main() { int j = 12; int *ptr = 0; cout << *ptr << endl; // crash ! return 0; } Segmentation violation (core dumped) Luca Lista - C++ 11 Puntatori e array • In C gli array sono trattati come puntatori X[0] 1.5 int main() { float x[5]; int j; for (j = 0; j < 5; j++) x[j] = 0; float *ptr *ptr = *(ptr+1) = *(ptr+3) = x X[1] 2.5 X[2] 0.0 X+1 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 } Luca Lista - C++ Puntatori: allocazione dinamica • Riferimento ad una locazione di memoria #include <iostream.h> 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 Luca Lista - C++ 12 Puntatori: allocazione dinamica • Riferimento a più locazioni di memoria #include <iostream.h> int main() { int *ptr = new int[3]; ptr[0] = 10; ptr[1] = 11; ptr[2] = 12 delete [] ptr; return 0; } ptr 10 11 12 Luca Lista - C++ 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 }; Luca Lista - C++ 13 Classe Vector • Una classe definisce oggetti che contengono insieme funzioni e dati • Un esempio: un vettore tridimensionale Vector.h class Vector Vector.cc { public: Vector(double x, double y, double z); #include “Vector.h” costruttore funzioni o metodi double double double double double double private: double x(); y(); z(); r(); phi(); theta(); x_, y_, z_; Vector::Vector(double x, double y, double z) : x_(x), y_(y), z_(z) { } double Vector::x() { return x_; } }; dati o attributi Punto e virgola! double Vector::r() { return sqrt( x_*x_ + y_*y_ + z_*z_); } Luca Lista - C++ Interfaccia e implementazione • Gli attributi privati non sono accessibili al di fuori della classe Vector.cc • I metodi pubblici sono #include “Vector.h” gli unici visibili Vector.hh class Vector { public: Vector(double x, double y, double z); double x(); double y(); double z(); double r(); double phi(); double theta(); private: double x_, y_, z_; }; Vector::Vector(double x, double y, double z) : x_(x), y_(y), z_(z) {} double Vector::x() { return x_; } double Vector::r() { return sqrt(x*x + y*y + z*z); } Luca Lista - C++ 14 Classe Vector • Come usare Vector: main.cc #include <iostream.h> #include “Vector.h” invoca il constructor int main() { Vector v(1, 1, 0); cout << “ v = (“ << v.x() << “,” << v.y() << “,” << v.z() << “)” << endl; cout << “ r = “ << v.r(); cout << “ theta = “ << v.theta() << endl; return 0; } Output: v = (1, 1, 0) r = 1.4141 theta = 1.5708 Luca Lista - C++ Interfaccia e implementazione • La struttura interna dei dati (x_, y_, z_) che rappresentano l’oggetto della classe Vector sono nascosti (private) ai client della classe. • I client non dipendono dalla struttura interna dei dati (come lo erano i client dei common block Fortran) • Se la struttura interna cambia (es.: r_, theta_, phi_), il codice che usa Vector non deve essere modificato. Luca Lista - C++ 15 Classe Vector • Protezione dell’accesso ai dati: main.cc #include <iostream.h> #include “Vector.h” int main() { Vector v(1, 1, 0); cout << << << << cout << cout << “ V = (“ v.x_ << “,” // v.y_ << “,” // non compila ! v.z_ << “)” << endl; // “ r = “ << v.r(); “ theta = “ << v.theta() << endl; } Luca Lista - C++ Selettori e modificatori • I Selettori (const) non modificano lo stato ( = gli attributi) dell’oggetto Vector.cc • I modificatori possono #include “Vector.h” modificare lo stato void { class Vector x_ { } public: Vector(double x, double y, double z); double x() const; double y() const; Selettori (const) double z() const; double r() const; double phi() const; double theta() const; void scale(double s); modificatore private: double x_, y_, z_; }; Vector.hh Vector::scale(double s) *= s; y_ *= s; z_ *= s; main.cc int main() { const Vector v(1, 0, 0); double r = v.r() // OK v.scale( 1.1 ); // errore! } Luca Lista - C++ 16 Argomenti delle funzioni • Un argomento può essere passato come valore o come riferimento • Passare due oggetti della classe Vector come valore (=copia) è dispendioso double dotProduct( Vector v1, Vector v2 ) { return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z(); } • Passare solo un riferimento (= puntatore) agli oggetti è più efficiente. Inoltre, è l’unico modo per poter modificare v1 e v2. double dotProduct( Vector& v1, Vector& v2 ) { return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z(); } Luca Lista - C++ Argomenti costanti • Per evitare di modificare un argomento passato come riferimento, si può dichiararlo const double dotProduct( const Vector& v1, const Vector& v2 ) { return v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z(); } double normalize( Vector& v ) { v.scale( 1 / v.r() ); } int main() { const Vector v( 1, 1, 0 ), w( 1, 0, 1 ); double vw = dotProduct( v, w ); normalize( v ); // errore: non compila! } Luca Lista - C++ 17 Operatori • E’ possibile ridefinire +, -, *, [], ++, ==, . . . Vector.hh Vector.cc class Vector { public: Vector(double x, double y, double z); double x() const; double y() const; double z() const; double r() const; double phi() const; double theta() const; private: double x_, y_, z_; }; Vector operator+(const const Vector operator-(const const Vector& Vector& Vector& Vector& Vector operator+( const Vector& v1, const Vector& v2) { return Vector(v1.x() + v2.x(), v1.y() + v2.y(), v1.z() + v2.z()); } Vector operator-( const Vector& v1, const Vector& v2) { return Vector(v1.x() - v2.x(), v1.y() - v2.y(), v1.z() - v2.z()); } v1, v2); v1, v2); Luca Lista - C++ Operatori • Esempio: main.cc #include <iostream.h> #include “Vector.h” int main() { Vector v1(1, 0, 0), v2(0, 1, 0); Vector v; Sintassi alternativa (!#@!?) : v.operator=(operator+(v1, v2)); v = v1 + v2; cout << “ v = “ << v << endl; cout << “ r = “ << v.r(); cout << “ theta = “ << v.theta() << endl; } Output: ridefinizione di << v = (1, 1, 0) r = 1.4141 theta = 1.5708 Luca Lista - C++ 18 Classe Array a dimensione fissa Array.h costruttore distruttore class Array { public: Array( int size ); ~Array(); int size() const; int& operator[]( int i ); private: int* array_; int size_; }; Array.cc #include <assert.h> #include “Array.h” Array::Array( int size ) : size_( size ) { array_ = new int[ size ]; } Array::~Array() { delete [] array_; } main.cc int& Array::operator[]( int i ) { assert( i >=0 && i < size_ ); return array_[i]; } int main() { Array a(2); a[0] = 1; a[1] = 2; } int Array::size() const { return size_; } Luca Lista - C++ Overloading di operatori • possono esistere funzioni con lo stesso nome ma con argomenti diversi Vector.hh Vector.cc class Vector { public: // ... }; Vector Vector::operator*(double s, const Vector v) { return Vector( s * v.x(), s * v.y(), s * v.z() ); } Vector operator*(double s, const Vector& v); double operator*(const Vector& v1, const Vector& v2); double Vector::operator*(const Vector& v1, const Vector& v2) { return ( v1.x() * v2.x() + v1.y() * v2.y() + v1.z() * v2.z() ); } Luca Lista - C++ 19 Operatori utilizzabili + += <<= 2 argomenti - -= == 1 argomento * *= != + / /= <= - % %= >= * ^ ^= && & & &= || -> | |= , ~ > >> () ! < << [] ++ = >>= Array v(3); int n = v[0]; Complex c(2, 2); Complex cConj = *c; Vector v; Point p; Vector u = v + p; cout << v; ->*Luca Lista - C++ -- Argomenti di default • E’ possibile specificare il default per gli argomenti delle funzioni Vector.h Vector.cc class Vector { public: Vector(double x = 0, double y = 0, double z = 0); . . . main.cc }; Vector::Vector(double x, double y, double z) : x_(x), y_(y), z_(z) { } . . . #include <iostream.h> #include “Vector.h” Output int main() { V = (0, 0, 0) Vector v; cout << “v = “ << v << endl; } Luca Lista - C++ 20 Funzioni e dati statici • Alcune funzioni e attributi possono essere comuni a tutta la classe Particle.h class Particle { public: Particle(int code, double mass, int charge); int code() const { return code_; } double mass() const { return mass_; } int charge() const { return charge_; } Particle.cc #include “Particle.h” int Particle::n_ = 0; int Particle::numberOfParticles() { return n_; } static int numberOfParticles(); Particle::Particle(int code, double mass, int charge) : code_(code), mass_(mass), charge_(charge) { n_ ++; } private: double mass_; int code_, charge_; static int n_; }; Luca Lista - C++ Classi astratte • Esempio classico: Shape • Tutti oggetti nella finestra hanno comportamenti comuni che possono essere considerati in astratto: – disegna, sposta, ingrandisci, etc. Luca Lista - C++ 21 Circle.cc Cerchio # Nome della classe include “Circle.h” Nome della classe Circle.h void Circle::draw() const { const int numberOfPoints = 100; class Circle { float x[numberOfPoints], y[numberOfPoints]; public: float phi = 0, deltaPhi = 2*M_PI/100; Circle(Point2d center, double radius); for ( int i = 0; i < numberOfPoints; ++i ) ~Circle(); { x[i] := classe center_.x() + radius_ * cos( phi ); void moveAt(const Point2d & p); Point2d che rappresenta y[i] = center_.y() + radius_ * sin( phi ); void moveBy(const Point2d & p); un punto in 2 dimensioni. phi += dphi; void scale(double s); } void rotate(double phi); polyline_draw(x, y, numberOfPoints ); void draw() const; } void cancel() const; private: void Circle::moveAt( const Point2d& p ) Point2d center_; { double radius_; cancel(); center_ = p; draw(); }; #include “Circle.h” } Costruttore Distruttore Main.cc Interfaccia Pubblica Metodi: Metodi: operazioni sugli oggetti int main() void Circle::scale( double s ) { “Dati” { privati Circle c( Point2d(10, 10), 5 ); c.draw(); c.moveAt(Point2d(20, 30)); Punto e virgola! virgola! cancel(); , membri)radius_ (Attributi, Attributi } *= s; draw(); Circle::Circle( Point2d c, double r ) : center_( c ), radius_( r ) { draw(); } return 0; } Circle::~Circle() Luca Lista - C++ { cancel(); } 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: Metodi: operazioni sugli oggetti “Dati” privati (Attributi, Attributi, membri) Punto e virgola! virgola! Luca Lista - C++ 22 Circle.cc Cerchio # include “Circle.h” 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 ); } Main.cc void Circle::moveAt( const Point2d& p ) { cancel(); center_ = p; draw(); } #include “Circle.h” int main() { Circle c( Point2d(10, 10), 5 ); void Circle::scale( double s ) { cancel(); radius_ *= s; draw(); } Punto e virgola! virgola! c.draw(); c.moveAt(Point2d(20, 30)); return 0; Circle::Circle( Point2d c, double r ) : center_( c ), radius_( r ) { draw(); } Circle::~Circle() Luca Lista - C++ } { Quadrato Square.h class Square { public: Square(const Point2d&, const Point2d&); ~Square(); 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_; Vector2d centerToUpperCorner_; }; centerToUpperCorner_ upperCorner cancel(); } Square.cc #include “Square.h” 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); } void Square::rotate( double phi ) { cancel(); centerToUpperCorner_.rotate( phi ); draw(); } Square::Square(const Point2d& lowerCorner, const Point2d& upperCorner ) : center_( median(lowerCorner, upperCorner) ), centerToUpperCorner_( upperCorner - center_ ) { draw(); } loweCorner void Square::scale( double s ) { cancel(); centerToUpperCorner_ *= s; draw(); } Luca Lista - C++ 23 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 Circle * circles[ 10 ]; cerchi, crea oggetti in memoria e salva 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() draw() per ogni elemento Come gestire cerchi e quadrati insieme? Luca Lista - C++ Polimorfismo Tutte le Shapes Shapes hanno la stessa interfaccia: draw, draw, pick, pick, move, move, fillColor... fillColor...,, ma ogni sottotipo diverso può avere la usa personale implementazione Luca Lista - C++ 24 Interfaccia astratta Shape.h class Shape { public: Shape() { } virtual ~Shape() { } virtual virtual virtual virtual virtual void void void void void Main.cc moveAt( const Point2d& ) = 0; scale( double s ) = 0; rotate( double phi ) = 0; draw() const = 0; cancel() const = 0; }; Interfaccia di metodi puramente virtuali Square.h #include “Circle.h” #include “Square.h” int main() { Shape * shapes[ 10 ]; int index = 0; for ( int i = 0; i < 5; 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 < 10; i++ ) shapes[ i ]->draw(); class Square : public Shape { // …. Il resto tutto uguale a prima }; return 0; } Luca Lista - C++ Ereditarietà e riuso del codice CenteredShape.h Class CenteredShape: public Shape { public: CenteredShape( Point2d c ) : center_( c ) { /*draw();*/ } ~Circle() { /*cancel();*/ } void moveAt( void moveBy( virtual void virtual void virtual void virtual void const Point2d& ); const Vector2d& ); scale( double ) = 0; rotate( double ) = 0; draw() const = 0; cancel() const = 0; protected: Point2d center_; }; Non si possono chiamare metodi virtuali in costruttori e distruttori (troppo presto, troppo tardi) Square.h #include “CenteredShape.hh” class Square : public CenteredShape { public: Square( Point2d lowerCorner, Point2d upperCorner ) : CenteredShape( median(lowerCorner, upperCorner) ), touc_(upperCorner - center_) { draw(); } ~Square() { cancel(); } 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_; Luca Lista - C++ }; 25 Modello UML Shape c lass i a stratt e draw() cancel() moveBy() relazione di aggregazione CenteredShape relazioni di ereditarietà Point2d moveBy() Ci rcle 1 1 Square radi us_ : doubl e CenterToUpperCorner_ : Vector2d draw() draw() classi concrete Luca Lista - C++ Vettore di classe astratta Shape* Main.cc #include “Circle.h” #include “Square.h” #include <vector> int main() { vector<Shape *> vs; // un vettore di puntatori a Shapes for ( int i = 0; i != 10; ++i ) { // aggiungiamo un puntatore ad un nuovo cerchio vs.push_back(new Circle(Point2d(i, i), 2.)); // aggiungiamo un puntatore ad un nuovo quadrato vs.push_back(new Square(Point2d(i, i), Point2d(i + 1, i + 2))); } // iteratore: essenzialmente un puntatore a Shape* ossia un Shape** vector<Shape *>::const_iterator p; for ( p = vs.begin(); p != vs.end(); ++p ) // loop sull’intero vettore (*p)->draw(); // disegniamo ogni Shape return 0; } Luca Lista - C++ 26 Superfici e traiettorie • Nel tracking spesso è necessario calcolare intersezioni tra curve (tracce) e superfici (elementi di detector) Intersection Trajectory Surface Line Plane PolyLine Cylinder Helix Luca Lista - C++ Superfici e traiettorie • Interfaccia delle diverse Trajectory Trajectory.h Line.h class Trajectory { public: virtual Point position(double s) = 0; virtual Vector direction(double s) = 0; }; #include “Trajectory.h” Helix.h class Line : public Trajectory { public: virtual Point position(double s); virtual Vector direction(double s); public: Point origin_; Vector direction_; }; #include “Trajectory.h” class Helix : public Trajectory { public: virtual Point position(double s); virtual Vector direction(double s); }; Luca Lista - C++ 27 Superfici e traiettorie • Implementazione Trajectory.cc #include “Trajectory.h” Helix.cc // … vuoto ... #include “Helix.h” Line.cc #include “Line.h” Helix::Helix() { } Line::Line(const Point& o, constVector& d) : origin_( o ), direction_( d.unit() ) { } Point Helix::position(double s) { // implementazione } Point Line::position(double s) { return ( origin_ + s * direction_ ); } Luca Lista - C++ Superfici e traiettorie • Interfaccia delle varie Surface Surface.h Plane.h distanza (con segno) di un punto dalla superficie class Surface { public: virtual distance(const Point& p) = 0; virtual derDist(const Point& p, const Vector& r) = 0; }; #include “Surface.h” class Plane : public Surface { public: virtual distance(const Point& p); virtual derDist(const Point& p, const Vector& r); protected: Point origin_; Vector norm_; double dist_; }; Cylinder.h #include “Surface.h” class Cylinder : public Surface { public: virtual distance(const Point& p); virtual derDist(const Point& p, const Vector& r); }; Luca Lista - C++ 28 Superfici e traiettorie • Surface è una classe astratta Plane.cc Surface.cc #include “Plane.h” #include “Surface.h” // vuoto Plane::distance(const Point& p) { return ( _dist - ( (p - origin_) * direction_) ); } Plane::derDist(const Point& p, const Vector& r) { return - r * _direction; } Cylinder.cc #include “Cylinder.h” Cylinder::distance(const Point& p) { /* . . . */ } Cylinder::derDist(const Point& p, const Vector& r) { /* . . . */ } Luca Lista - C++ Superfici e traiettorie • Interfaccia di Intersection Intersection.h forward class declaration class Surface; class Trajectory; class Intersection { public: Intersection(Surface* s, Trajectory* t) surface_(s), trajectory_(t) {} Point intersect(double s1, double s2); private: double sIntersect(double s1, double s2); Surface* surface_; Trajectory* trajectory_; }; Luca Lista - C++ 29 Superfici e traiettorie Intersection.cc #include #include #include #include • Implementazione dell’algoritmo “Intersection.h” <math.h> “Surface.h” “Trajectory.h” // controlla che test è tra s1 e s2 if( (s1 - test) * (test - s2) < 0.0 ) { if ( s1 < s2 ) s += abs( d ); else s -= abs( d ); if( s > maxS || s < minS ) return sMax; } else s = test; const int maxIterations 20 const double sMax 1.e+6 const double accuracy1.e-3 double Intersection::sIntersect(double s1, double s2) { // algoritmo di Newton-Raphson double s = s1; double maxS = max(s1, s2); double minS = min(s1, s2); double d, delta; for( int j = 0; j < maxIterations; j++ ) { Point p = _trajectory->position( s ); d = surface_->distance( p ); delta = surface_->derDist( p, trajectory_->direction( s ) ); double ds = - d / delta; double test = s + ds; if( abs(d) < accuracy ) return s; } return sMax; } Point Intersection::intersect(double s1, double s2) { return trajectory_->position(sIntersect(s1, s2)); } Luca Lista - C++ Superfici e traiettorie • Intersection usa solo: – I metodi position e direction di un’oggetto Trajectory – I metodi distance e derDist di un oggetto Surface • E’ possibile aggiungere una nuova classe che modellizza una nuova Trajectory o una nuova Surface e Intersection continua a funzionare senza modificare una linea di codice! • E’ possibile rendere anche Intersection astratto... Luca Lista - C++ 30 “Open/Closed principle” • Un buon codice deve essere – aperto ad estensioni – chiuso a modifiche • Modificare un codice funzionante può introdurre bachi… • L’Object Oriented, con il meccanismo delle classi virtuali, permette di applicare questo principio Luca Lista - C++ Programmazione generica • La stessa definizione di funzione si può applicare a tipi diversi template<class T> T sqr(const T x) { return x * x } int i = 1, j = 3; int n = max( 3, 2 ); String a = “Mela”, b = “Pera”; String c = max( a, b ); Matrix m; Matrix mSqr = sqr( m ); template<class T> T min(const T a, const T b) { return (a < b ? a : b); } template<class T> T max(const T a, const T b) { return (a < b ? b : a); } Per il tipo T deve essere definito l’operatore < Luca Lista - C++ 31 Sintassi Sintassi • template < class identifier > function definition • template < class identifier > class definition Ognivolta voltache chenella nelladefinizione definizionedella dellafunzione funzioneoo Ogni dellaclasse classeappare appareidentifier identifierquesto questoviene vienesostituito sostituito della dal compilatore con il tipo fornito nella chiamata. dal compilatore con il tipo fornito nella chiamata. Ladichiarazione dichiarazioneeel’implementazione l’implementazione La deltemplate templatedevono devonoessere esserenello nellostesso stesso del file ove il template viene utilizzato file ove il template viene utilizzato Luca Lista - C++ Un Un esempio: esempio: lolo stack stack di di interi interi Usercode code User class Stack Stack {{ class public: public: Stack() {top {top == NULL;} NULL;} Stack() ~Stack() {;} {;} ~Stack() void push push (( int int ii )) {{ void Contenuto* tmp tmp == new new Contenuto* Contenuto(i,top ); ); Contenuto(i,top top == tmp; tmp; }} top int pop pop () () {{ int int ret ret == top->GetVal(); top->GetVal(); int Contenuto* tmp tmp == top; top; Contenuto* top == top->GetNext(); top->GetNext(); top delete tmp; tmp; delete return ret; ret; return }} private: private: Contenuto* top; top; Contenuto* }; }; int main() main() {{ int Stack s; s; Stack s.push (( 10 10 ); ); s.push s.push (( 20 20 ); ); s.push cout << s.pop() << ““ -- ““ << << s.pop(); s.pop(); cout << s.pop() << return 0; 0; return }; }; class Contenuto Contenuto {{ class public: public: Contenuto (( int int i, i, Contenuto* Contenuto* ptn ptn )) {{ Contenuto val=i; next=ptn; next=ptn; }} val=i; int GetVal GetVal (){ (){ return return val; val; }} int Contenuto* GetNext() GetNext() {return {return next;} next;} Contenuto* private: private: Contenuto* next; next; Contenuto* int val; val; int }; }; Output Output Luca Lista - C++ >>20 20--10 10 >> 32 Lo Lo stack stack ““templizzato” templizzato” Usercode code User template <class <class T> T> template class Stack { class Stack { public: public: Stack() {top {top == NULL;} NULL;} Stack() ~Stack() {;} {;} ~Stack() void push push (( TT ii )) {{ void Contenuto<T>* tmp == new new Contenuto<T>* tmp Contenuto<T> (i,top (i,top ); ); Contenuto<T> top == tmp; tmp; }} top pop () () {{ TT pop ret == top->GetVal(); top->GetVal(); TT ret Contenuto<T>* tmp == top; top; Contenuto<T>* tmp top == top->GetNext(); top->GetNext(); top delete tmp; tmp; delete return ret; ret; return }} private: private: Contenuto<T>* top; top; Contenuto<T>* }; }; int main() main() {{ int Stack<int> s; s; Stack<int> s.push (( 10 10 ); ); s.push s.push (( 20 20 ); ); s.push Stack<double> s1; Stack<double> s1; Stack<Shape> s2; s2; Stack<Shape> cout << << s.pop() s.pop() << << ““ ““ << << s.pop(); s.pop(); cout return 0;}; 0;}; return template <class <class T> T> template class Contenuto class Contenuto {{ public: public: Contenuto (( TT i, i, Contenuto* Contenuto* Contenuto ptn )) {{ val val == i; i; next next == ptn; ptn; }} ptn GetVal (){ (){ return return val; val; }} TT GetVal Contenuto* GetNext() GetNext() {return {return Contenuto* next;} next;} private: private: Contenuto* next; next; Contenuto* val; TT val; }; Luca Lista - C++ }; Linked List List _last • Contenitore di oggetti dello stesso tipo • Ogni nodo ha un puntatore al successivo; Node l’ultimo nodo punta _next al primo, e chiude la catena _datum _next 10 Node Node _next _next _datum _next _datum _next 11 12 Luca Lista - C++ 33 Linked List: interfaccia #ifndef LIST_H #define LIST_H template<class T> class List direttive al preprocessore { public: per evitare inclusione multipla List(); ~List(); void append(T *x); T* operator[](int) const; int length() const; private: class Node { public: Node(T *x) : _next(0), _datum(x) {} Node* _next; T* _datum; }; Node* _last; int _length; friend class ListIterator<T>; // vedi dopo... }; #endif Luca Lista - C++ Linked List: implementazione #include "List.h” template<class T> List<T>::List() : _last(0), _length(0) {} template<class T> void List<T>::append(T *x) { if (_last == 0) { _last = new Node(x); _last->_next = _last; } else { Node* first = _last->_next; _last->_next = new Node(x); _last = _last->_next; _last->_next = first; } _length++; } template<class T> int List<T>::length() const { return _length; } template<class T> T* List<T>::operator[](int i) const { if (i >= _length || i < 0) return 0; else { Node* node = _last->_next; while(i-- > 0) node = node->_next; return node->_datum; } } L’uso di [] è inefficiente template<class T> List<T>::~List() { if (_last == 0) return; while (_last != _last->_next) { Node* last = _last; _last = _last->_next; delete last; } delete _last; Luca Lista - C++ } 34 Linked List: uso #include <iostream.h> #include "List.h” int main() { List<int> list; int i = 10, j = 100, k = 1000; list.append(&i); list.append(&j); list.append(&k); // uso inefficiente! for(i = 0; i < 3; i++) { cout << *list[i]; } return 0; } Luca Lista - C++ Iteratore sulla lista #ifndef LISTITERATOR_H #define LISTITERATOR_H #include "List.h” template<class T> class ListIterator { public: ListIterator(const List<T>& list); T* current() const; bool next(); void rewind(); private: Aggiungere: const List<T> * _list; friend class ListIterator<T>; List<T>::Node* _current; }; all’header file List.h ! #endif Luca Lista - C++ 35 Iteratore sulla lista #include "ListIterator.h” template<class T> bool ListIterator<T>::next() { // lista vuota if (_list->_last == 0) return false; template<class T> ListIterator<T>::ListIterator (const List<T>& list) : _list(&list), _current(0) { } if (_current == 0) { _current = _list->_last->_next; return true; } else { _current = _current->_next; if(_current != _list->_last->_next) return true; else return false; } template<class T> T* ListIterator<T>::current() const { if (_current == 0) return 0; else return _current->_datum; } template<class T> void ListIterator<T>::rewind() { _current = 0; } } Luca Lista - C++ Iteratore sulla lista • Uso di List e ListIterator #include <iostream.h> #include "List.h" #include "ListIterator.h” int main() { List<int> list; int i = 10, j = 100, k = 1000; list.append(&i); list.append(&j); list.append(&k); // uso inefficiente! for(i = 0; i < 3; i++) { cout << *list[i]; } // iterazione efficiente ListIterator<int> it(list); while(it.next()) cout << *(it.current()) << endl; return 0; } Luca Lista - C++ 36 Container • • • • • • • List OrderedList, Set Dictionary Stack Fifo ... Luca Lista - C++ La La Standard Standard Library 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 puntatoriintelligenti intelligenti puntatori vettori,liste, liste,mappe, mappe,…. …. vettori, find,replace, replace,reverse, reverse,sort, sort,…. …. find, Luca Lista - C++ 37 Iteratori Iteratori (puntatori (puntatori intelligenti) 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 Luca Lista - C++ Contenitori Contenitori Un contenitore e’ un oggetto capace di immagazzinare altri oggetti (i suoi elementi) e che possiede metodi per accedere ai suoi elementi. Ogni contenitore ha un iteratore associato che permette di muoversi tra gli elementi del contenitore. Sequenze: Una sequenza e’ un contenitore di lunghezza variabile i cui elementi sono organizzati linearmente. E’ possibile aggiungere e rimuovere elementi Contenitori associativi: Una sequenza e’ un contenitore di lunghezza variabile che permette un efficiente accesso ai suoi elementi basato su una chiave. Luca Lista - C++ 38 Sequenze 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 Luca Lista - C++ Contenitori Contenitori associativi 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 •hash_map : • Viene richiesta una funzione di hash per la chiave • Non vi e’ alcun ordinamento Luca Lista - C++ 39 Algoritmi Algoritmi Gli algoritmi sono delle funzioni globali capaci di agire su contenitori differenti find sort 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...). Luca Lista - C++ Esempio Esempio uso uso sequenze sequenze list > #include <vector #include <algo.h> #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; find ( container.begin(), container.end(), 3 ); sort ( container.begin(), container.end() ); for ( it1=container.begin(); it1!=container.end(); it1++) cout << "vector : " << *it1 << endl; min_element ( container.begin(), container.end()); return 0; } Luca Lista - C++ 40 Un semplice vettore begin() begin() end() end() 0 1 2 ... 9 p p p p p p Luca Lista - C++ Esempio Esempio uso uso contenitori contenitori associativi associativi #include <map> #include <algo.h> #include <iostream> int main() { map<char*,int> amap; amap.insert( pair<char*,int>("Primo",1)); amap.insert( pair<char*,int>("Secondo",2)); cout << "Size : " << amap.size() << endl; amap["Terzo"]=3; amap["Quarto"]=4; cout << "Size : " << amap.size() << endl; map<char*,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; } Luca Lista - C++ 41 Fine Luca Lista - C++ 42