1 ereditarietà una classe può essere derivata da una classe esistente usando la sintassi: public,...

21
1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai membri della classe protected vuol dire che i campi sono accessibili da una sottoclasse, ma non da altre classi esterne (sta fra public e private, è come se la sottoclasse fosse friend della classe di base per quei campi) class newclass: (public|protected|private) oldclass { dichiarazioni... };

Upload: cipriana-sacco

Post on 01-May-2015

216 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

1

EreditarietàEreditarietà• Una classe può essere derivata da una classe

esistente usando la sintassi:• public, protected e private specificano il tipo

di accesso ai membri della classe

• protected vuol dire che i campi sono accessibili da una sottoclasse, ma non da altre classi esterne (sta fra public e private, è come se la sottoclasse fosse friend della classe di base per quei campi)

class newclass: (public|protected|private) oldclass {dichiarazioni...};

Page 2: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

2

Ereditarietà (2)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

Page 3: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

3

Ereditarietà (3)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

Page 4: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

4

Classi base astratteClassi base astratte• Una funzione puramente virtuale è un metodo

virtuale non definito. E` dichiarato come:

• 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

virtual func_prototype = 0;

Page 5: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

5

Esempio: i soldatiEsempio: 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.

Page 6: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

6

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()?

// soluzione la dichiaro virual = 0!!!! }};

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()?

// soluzione la dichiaro virual = 0!!!! }};

Page 7: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

7

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

}

};

...

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

}

};

...

Page 8: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

8

Erediarietà multiplaErediarietà 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

• L’ ereditarietà multipla viene spesso utilizzata per combinare un’interfaccia ed una implementazione, ma è molte volte sintomo di un cattivo disegno

class A {. . . .};class B {. . . .};

class AplusB: public A, private B {. . . .};

Page 9: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

9

Ereditarietà (4)Ereditarietà (4)• Una classe derivata estende la classe base e ne

eredita tutti i metodi e gli attributi

class Track{public: LorentzVector momentum() { return p_; }

protected: LorentzVector p_;};

class Track{public: LorentzVector momentum() { return p_; }

protected: LorentzVector p_;};

Track.hTrack.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_;};

#include “Track.h”

class DchTrack : public Track{public: int hits() { return hits_->size(); }

DchHit* hit(int n) { return hits_[n]; }

protected: list<DchHit> hits_;};

DchTrack.hDchTrack.h

DchTrack è una Track che ha degli attributi in più (hits_) e nuovi metodi (DchHit* hit(int n), int hits())

Page 10: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

10

Esempio: shapeEsempio: shape

• Tutti gli oggetti nella finestra hanno comportamenti comuni che possono essere considerati in astratto:– disegna

– sposta

– ingrandisc

– etc...

Page 11: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

11

Cerchi e quadratiCerchi e quadrati

Quadrato Cerchio

Page 12: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

12

Circle.h

CerchioCerchio

CostruttoreCostruttore

DistruttoreDistruttore

Nome della classeNome della classe

Punto e virgolaPunto e virgola!!

Point2dPoint2d: classe che rappresenta : classe che rappresenta un punto in 2 dimensioni.un punto in 2 dimensioni.

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;

““Dati” privatiDati” privati((AttributiAttributi, membri), membri)

class Circle {

};

private: Point2d center_; double radius_;

Interfaccia PubblicaInterfaccia PubblicaMetodiMetodi: operazioni sugli : operazioni sugli

oggettioggetti

Page 13: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

13

Cerchio (2)Cerchio (2)#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, 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(); }

Circle::~Circle() { cancel(); }

Circle.cc

#include “Circle.h”int main() { Circle c( Point2d(10, 10), 5 ); c.draw(); c.moveAt(Point2d(20, 30)); return 0;}

Main.cc

Page 14: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

14

Quadrato Quadrato

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_;};

Square.h#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, 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; draw(); }

Square.cc

upperCornerupperCorner

loweCornerloweCorner

centerToUpperCornercenterToUpperCorner__

Page 15: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

15

Codice Applicativo (Client)Codice Applicativo (Client)

#include “Circle.h”#include “Square.h”

int main() { Circle c1( Point2d(2.,3.), 4.23 ); Square r1( Point2d(2.,1.), Point2d(4.,3.) );

Circle * circles[ 10 ]; for ( int i = 0; i < 10; ++i ) { circles[ i ] = new Circle( Point2d(i,i), 2. ); } for ( int i = 0; i < 10; ++i ) circles[ i ]->draw();

return 0;}

Main.cc

Come gestire cerchi Come gestire cerchi e quadrati insieme?e quadrati insieme?

Costruisce un vettore di puntatori a Costruisce un vettore di puntatori a cerchi, crea oggetti in memoria e salva cerchi, crea oggetti in memoria e salva i loro puntatori nel vettore. i loro puntatori nel vettore.

Itera sul vettore e invoca Itera sul vettore e invoca drawdraw()() per ogni elementoper ogni elemento

Page 16: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

16

PolimorfismoPolimorfismo

Tutte le Tutte le ShapeShapesshanno la stessa interfaccia:hanno la stessa interfaccia:draw, pick, move, fillColor...draw, pick, move, fillColor...,,ma ogni ma ogni sottotiposottotipo diverso diversopuò avere la usa personalepuò avere la usa personaleimplementazioneimplementazione

Page 17: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

17

class Shape {public: Shape() { } virtual ~Shape() { } virtual void moveAt(const Point2d& where) = 0; virtual void changeColor(Color newColor) = 0; virtual void scale(double s) = 0; virtual void rotate(double phi) = 0; virtual void draw() const = 0; virtual void cancel() const = 0;};

Shape.h

Interfaccia astrattaInterfaccia astratta

Interfaccia di metodi Interfaccia di metodi puramente virtualipuramente virtuali

#include “Shape.h”

class Square : public Shape { // …. Il resto tutto uguale a prima};

Square.h

#include “Circle.h”#include “Square.h”

int main() { Shape * shapes[ 20 ]; 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; }

for ( int i = 0; i < 20; i++ ) shapes[ i ]->draw();

return 0;}

Main.cc

Page 18: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

18

Ereditarietà e riuso del codiceEreditarietà e riuso del codice

Class CenteredShape: public Shape {public: CenteredShape(Point2d c, Color color = TRASPARENT) : center_(c), color_(color) { /*draw();*/ } ~Circle() { /*cancel();*/ } void moveAt( const Point2d& ); void moveBy( const Vector2d& ); void changeColor( Color ); virtual void scale( double ) = 0; virtual void rotate( double ) = 0; virtual void draw() const = 0; virtual void cancel() const = 0;

protected: Point2d center_; Color color_;};

CenteredShape.hNon si possono chiamare Non si possono chiamare metodi virtuali in costruttori e metodi virtuali in costruttori e distruttori distruttori (troppo presto, (troppo presto, troppo tardi)troppo tardi)

#include “CenteredShape.hh”

class Square : public CenteredShape {public: Square( Point2d lowerCorner, Point2d upperCorner, Color col = TRASPARENT) : CenteredShape( median(lowerCorner, upperCorner), col), 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_;};

Square.h

Page 19: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

19

Attenzione alle generalizzazioni...Attenzione alle generalizzazioni...

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_; double lx_, ly_;};

Rectangle.h • Attenzione: scegliere le relazioni di ereditarietà può essere non banale.

• Un quadrato è un rettangolo?

class Square : public Rectangle{public: Square(double x0, double y0, double l) : Rectangle(x0, y0, l, l) { }};

Square.h

Avere lx_ e ly_ è ridondante per SquareCosa succede se si invoca scaleX o scaleY ?

Avere lx_ e ly_ è ridondante per SquareCosa succede se si invoca scaleX o scaleY ?

Page 20: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

20

Ereditarietà multiplaEreditarietà multipla• Una classe può ereditare da più classi

class DrawableObj{public: virtual void draw() = 0;};

DrawableObj.h

class Shape {public: virtual void scale(double s) = 0; virtual void moveAt( Vector2d& ) = 0;};

Shape.h

class DrawableShape : public DrawableObj, public Shape{public: virtual void draw(); virtual void scale(double s); virtual void moveAt( Vector2d& ); };

DrawableShape.h

Page 21: 1 Ereditarietà Una classe può essere derivata da una classe esistente usando la sintassi: public, protected e private specificano il tipo di accesso ai

21

PolimorfismoPolimorfismo

• Polimorfismo con tipi controllati dal compilatore

Come?• In C++ viene implementato tramite il concetto

di ereditarietà (inheritance)• Classe astratta: definisce i metodi• Classe concreta: implementa i metodi

La classe concreta eredita da quella astratta