Ereditarietà • Una classe può essere derivata da una classe esistente usando la sintassi: class newclass: (public|protected|private) oldclass il{tipo • public, protected e private specificano dichiarazioni... 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) 1 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 2 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 3 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 4 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. 5 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!!!! } }; 6 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 } }; ... 7 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 8 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()) 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_; }; 9 Esempio: shape • Tutti gli oggetti nella finestra hanno comportamenti comuni che possono essere considerati in astratto: – – – – disegna sposta ingrandisc etc... 10 Cerchi e quadrati Quadrato Cerchio 11 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! 12 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(); } 13 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_ loweCorner upperCorner 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(); } 14 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? 15 Polimorfismo Tutte le Shapes hanno la stessa interfaccia: draw, pick, move, fillColor..., ma ogni sottotipo diverso può avere la usa personale implementazione 16 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 17 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_; }; 18 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 ? 19 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& ); }; 20 Polimorfismo • 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 21