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
Scarica

Lezione sull`ereditarieta` - ICAR-CNR