Ingegneria Elettronica
Ingegneria delle Telecomunicazioni (J-Z)
Ing. Antonio Monteleone
A.A. 2001/02 – 3° ciclo
• L’ereditarietà permette di definire una nuova classe a partire da classi già definite.
La nuova classe, detta classe derivata , eredita le funzionalità da una o più classi base da cui deriva ma introduce delle nuove funzionalità che la caratterizzano e la distinguono dalle sue classi base.
• L’ereditarietà può essere singola o multipla
class Derivata : public|protected|private Base { // dichiarazioni...
};
tipo di ereditarietà
• a
#include “point.h”
class Polygon {
int m_vertexCount;
Point *m_vertexList;
public:
Polygon(int vertexCount, const Point *vertexList);
Polygon(const Polygon & p);
~ Polygon();
void translate(double lx, double ly);
void rotate(const Point ¢er, double angle);
double perimeter();
};
#include “polygon.h”
class Rectangle : public Polygon { double m_side1, m_side2;
public:
Rectangle(const Point &c, double s1, double s2, double angle);
~ Rectangle();
double diagonal();
Ereditarietà: lo specificatore d’accesso protected
• I membri protected di una classe sono accessibili soltanto dalle funzioni membro o friend di tale
classe e delle sue classi derivate
class Base { int i;
protected:
int k;
int read() const {return i;}
void set(int ii) { i = ii;
}
public:
Base(int ii = 0) : i(ii), k(0) {}
int value(int m) const { return m*i;
} };
class Derived : public Base { int j;
public:
Derived(int jj = 0) : j(jj) {}
void change(int x) {
k = x*x; // OK:k è protected set(x);
}
}; int main() { Base b;
b.set(20); //non compila Derived d;
d.change(10);
return 0;
}
Ereditarietà public, protected e private
• Una classe base può essere ereditata come
– class Derived : public Base { /* … */};
I membri public della classe base diventano membri public della classe derivata.
I membri protected della classe base diventano membri protected della classe derivata
– class Derived : protected Base { /* … */};
I membri public e protected della classe base diventano membri protected della classe derivata – class Derived : private Base { /* … */};
I membri public e protected della classe base
diventano membri private della classe derivata
Ereditarietà public, protected e private (2)
• Il tipo di ereditarietà di default è quello private
• L’ereditarietà di tipo private permette di
implementare una classe “in termini” della sua classe base
class Derived : Base { // dichiarazioni...
};
L’ereditarietà è di tipo private
Ereditarietà public, protected e private (3)
• L’ereditarietà di tipo public permette di esprimere relazioni is_a tra oggetti
• Nella ereditarietà di tipo public un oggetto di classe derivata può essere usato ovunque può essere usato un oggetto di classe base
class D: public B { // dichiarazioni...
};
Ogni oggetto di tipo D è anche un oggetto di tipo B, ma non viceversa
void f(const B &b) { /*... */ } void g(const D &d) { /*... */ }
void main() { D d;
B b;
f(d); // OK: d è anche un B g(b); // ERR: b non è un D };
Costruttori di classi derivate (1)
• Un oggetto di una classe derivata contiene un sotto-oggetto appartenente alla sua classe base
• Quando si istanzia un oggetto di una classe derivata viene chiamato prima l’opportuno
costruttore della classe base e poi l’opportuno
costruttore della classe derivata
Costruttori di classi derivate (2)
• Quando si crea un oggetto di una classe derivata le variabili membro della classe base sono inizializzate mediante l’invocazione di un costruttore di tale
classe nella lista di inizializzatori
class Person { char *m_name;
public:
Person(char *name);
// altre dichiarazioni … char *name();
};
class Employ : public Person { float m_salary;
public:
Employ(char *name, float salary);
// altre dichiarazioni … float salary();
};
Employ::Employ(char *name, float salary) : Person(name)
, m_salary(salary) {}
Chiamata esplicita al costruttore della classe base
Costruttori di classi derivate (3)
• Se nella costruzione di un oggetto di una classe derivata si omette la chiamata di un costruttore della classe base, il compilatore provvederà a chiamare il costruttore di default di tale classe
class Base { int i;
public:
Base(int ii = 0): i(ii){}
// altre dichiarazioni … };
class Derived : public Base { int j;
public:
Derived(int jj = 0);
// altre dichiarazioni … };
Derived::Derived(int jj) : j(jj) {}
Chiamata implicita al costruttore di default della classe base
Distruttori di classi derivate
• Quando viene distrutto un oggetto di una classe derivata il distruttore di tale classe viene invocato prima del distruttore di ogni sua classe base
• Il distruttore di una classe base viene chiamato automaticamente, ossia non bisogna invocarlo esplicitamente dal distruttore della classe derivata
class Base { public:
// altre dichiarazioni …
~Base();
}; int main() {
Derived derived;
// …
return 0;
class Derived : public Base { char *a;
public:
// altre dichiarazioni …
~Derived() {
if (a) delete a;
} };
La ridefinizione di funzioni membro
• In una classe derivata è possibile ridefinire una funzione membro di una sua classe base
• Per invocare la funzione membro della classe base si utilizza l’operatore ::
class Base { public:
// altre dichiarazioni … void f(int a) {
cout << “Base::f” << endl;
// … }
}; int main() {
Derived derived;
derived.f(1);
derived.Base::f(2);
return 0;
}
class Derived : public Base { public:
// altre dichiarazioni … void f(int a) {
cout << “Derived::f” << endl;
// … }
};
• In C++ è possibile far derivare una classe da più classi base
class Base1 { int ii;
public:
Base1(int i = 0) : ii(i) {}
int value() const { return ii;
} };
class Base2 { char cc;
public:
Base2(char c = 0) : cc(c) {}
char value() const { return cc;
}
class Derived : public Base1 , public Base2 { float jj;
public:
Derived(int i, char c, float j) : Base1(i), Base2(c), jj(j) {}
float getValue() { return jj
}
}; void main() {
Derived d(1,’a’,2.5);
cout << d.Base1::value();
cout << d.Base2::value();
Base1 *pb1 = &d;
cout << pb1->value();
}
Conversioni tra classi base e derivata (1)
• Nell’ereditarietà di tipo public un oggetto di
classe derivata è anche un oggetto di classe base
• In generale non è possibile assegnare un oggetto di classe base a un oggetto di classe derivata
Employ emp(“Rossi”, 1000);
Person p(“Bianchi”);
p = emp;
la classe derivata contiene tutti i membri della classe base
Employ emp(“Rossi”, 1000);
Person p(“Bianchi”);
emp = p; //errore di compilazione
la classe derivata contiene dei membri non appartenenti alla classe base
Conversioni tra classi base e derivata (2)
• Nell’ereditarietà di tipo public un puntatore (un riferimento) a un oggetto di classe derivata può essere convertito implicitamente in un puntatore (un riferimento) a un oggetto di classe base (up- cast)
Employ emp(“Rossi”, 1000);
Person *p = &emp;
cout << p->name();
cout << p->salary(); //errore
L’accesso a un oggetto di classe derivata attraverso un puntatore a una sua
classe base è limitato
solo ai membri pubblici di tale classe
void f(const Person &p) { /*... */ } void main() {
Employ emp(“Rossi”, 1000);
f(emp); // up-cast implicito
Conversioni tra classi base e derivata (3)
• Un puntatore (un riferimento) a un oggetto di classe base può essere convertito solo
esplicitamente in un puntatore (un riferimento) a un oggetto di classe derivata (down-cast)
• Un down-cast va effettuato solo se l’oggetto puntato dal puntatore di classe base è
effettivamente un oggetto di classe derivata
Person p(“Rossi”);
Employ *e1 = &p; //errore di compilazione Employ *e2 = static_cast< Employ *>(&p);
cout << e2->name();
cout << e2->salary(); //errore logico
class Base {
// dichiarazioni … };
class Derived : public Base { // altre dichiarazioni …
void fun() ; // non e’ definita in Base };
void g(Base *ptr) {
ptr->fun(); // Errore di compilazione
Derived *p = dynamic_cast<Derived *> (ptr);
if (p!=0) p->fun();
}
dynamic_cast
• Il dynamic_cast opera una conversione, se è possibile, fra due tipi. Il puntatore ritornato non è
nullo soltanto se il tipo dell’oggetto su cui si opera è quello che ci si aspetta
void main() {
Base *b1 = new Base();
g(b1); // fun() non è chiamata Base *b2 = new Derived();
g(b2); // fun() è chiamata }