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