1 / 21

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

keilah
Télécharger la présentation

Ereditarietà

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  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

  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

  4. 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;

  5. 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.

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

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

  8. 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 { . . . . };

  9. 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())

  10. Esempio: shape • Tutti gli oggetti nella finestra hanno comportamenti comuni che possono essere considerati in astratto: • disegna • sposta • ingrandisc • etc...

  11. Cerchi e quadrati Quadrato Cerchio

  12. 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_;

  13. 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)

  14. 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

  15. 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?

  16. Polimorfismo Tutte le Shapes hanno la stessa interfaccia: draw, pick, move, fillColor..., ma ogni sottotipo diverso può avere la usa personale implementazione

  17. 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

  18. 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)

  19. 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 ?

  20. 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

  21. 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

More Related