210 likes | 350 Views
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
E N D
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... };
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
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
Classi 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’interfacciadi una categoria di classi virtual func_prototype = 0;
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.
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!!!! } };
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 } }; ...
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 • 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 { . . . . };
Track.h class Track { public: LorentzVector momentum() { return p_; } protected: LorentzVector p_; }; 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_; }; Ereditarietà (4) • Una classe derivata estende la classe base e ne eredita tutti i metodi e gli attributi DchTrackè una Track che ha degli attributi in più (hits_) e nuovi metodi (DchHit* hit(int n), int hits())
Esempio: shape • Tutti gli oggetti nella finestra hanno comportamenti comuni che possono essere considerati in astratto: • disegna • sposta • ingrandisc • etc...
Cerchi e quadrati Quadrato Cerchio
Nome della classe Circle.h Costruttore Distruttore Point2d: classe che rappresenta un punto in 2 dimensioni. Interfaccia Pubblica Metodi: operazioni sugli oggetti “Dati” privati (Attributi, membri) Punto e virgola! Cerchio 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_;
Circle.cc Main.cc #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(); } #include “Circle.h” int main() { Circlec( Point2d(10, 10), 5 ); c.draw(); c.moveAt(Point2d(20, 30)); return 0; } Cerchio (2)
Square.h Square.cc 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_; }; #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(); } Quadrato centerToUpperCorner_ upperCorner loweCorner
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.) ); 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; } Codice Applicativo (Client) Costruisce un vettore di puntatori a cerchi, crea oggetti in memoria e salva i loro puntatori nel vettore. Itera sul vettore e invoca draw() per ogni elemento Come gestire cerchi e quadrati insieme?
Polimorfismo Tutte le Shapes hanno la stessa interfaccia: draw, pick, move, fillColor..., ma ogni sottotipo diverso può avere la usa personale implementazione
Shape.h Square.h Main.cc 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; }; #include “Shape.h” class Square: publicShape { // …. Il resto tutto uguale a prima }; #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; } Interfaccia astratta Interfaccia di metodi puramente virtuali
CenteredShape.h Square.h 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_; }; #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_; }; Ereditarietà e riuso del codice Non si possono chiamare metodi virtuali in costruttori e distruttori (troppo presto, troppo tardi)
Rectangle.h Square.h class Square : public Rectangle { public: Square(double x0, double y0, double l) : Rectangle(x0, y0, l, l) { } }; 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_; }; Attenzione alle generalizzazioni... • Attenzione: scegliere le relazioni di ereditarietà può essere non banale. • Un quadrato è un rettangolo? Avere lx_ e ly_ è ridondante per Square Cosa succede se si invoca scaleX o scaleY ?
DrawableObj.h Shape.h DrawableShape.h class DrawableShape : public DrawableObj, public Shape { public: virtual void draw(); virtual void scale(double s); virtual void moveAt( Vector2d& ); }; class DrawableObj { public: virtual void draw() = 0; }; class Shape { public: virtual void scale(double s) = 0; virtual void moveAt( Vector2d& ) = 0;}; Ereditarietà multipla • Una classe può ereditare da più classi
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