1 FONDAMENTI DI INFORMATICA II Ingegneria Gestionale a.a ° Ciclo Ereditarietà
2 Classe Base Classe Derivata Ereditarietà singola: La classe derivata eredita dati e funzioni membro della classe base, sovrascrivendo e migliorando le caratteristiche di quest’ultime e aggiungendo altri dai e funzioni. La classe derivata è più specifica della classe base e rappresenta perciò una classe di oggetti meno estesa (ad esempio la classe dei quadrati può considerarsi derivata dalla classe dei quadrilateri, ma è meno estesa della classe dei quadrilateri)
3 Ereditarietà Classe Base AClasse Base CClasse Base B Classe Derivata Ereditarietà Multipla: La classe derivata eredita dati e funzioni da più di una classe base. …………….
4 Ereditarietà Tipi di ereditarietà: L’ereditarietà può essere di tipo public, private, o protected. Per ora si studierà il caso dell’ereditarietà di tipo public in cui è possibile trattare allo stesso modo un oggetto della classe derivata e uno della classe base. Dichiarazione di ereditarietà public: Per dichiarare che una classe è derivata di tipo public da una classe base essa deve essere dichiarata nel seguente modo: class Nome_Classe_Derivata : public Nome_Classe_Base {.. …………..};
5 Ereditarietà Esempio: class Punto { public: Punto (x=0, y=0); void printPunto ( ); private: float x, y;}; ……………... ……………… class Segmento: public Punto { Segmento (float x=0., float y=0., float x1=0., float y2=0.); ………….. };
6 Ereditarietà Accedibilità dei membri Il tipo di ereditarietà comporta differenti gradi di accedibilità ai membri della classe base. Questi ultimi possono essere dichiarati, oltre che public e private, come abbiamo visto finora, anche protected, che, come vedremo rappresenta un grado intermedio di accedibilità. Nel caso di ereditarietà public si hanno le seguenti regole di accedibilità ai membri della classe base: Membri public: dalle funzioni membro della classe, dalle funzioni friend e da tutte le funzioni del programma (incluse quelle della classe derivata). Membri protected: dalle funzioni membro della classe, dalle funzioni friend e da quelle della classe derivata. Membri private: dalle funzioni membro della classe e dalle funzioni friend.
7 Ereditarietà Costruttore di una classe derivata Il costruttore di una classe derivata, poiché essa eredita tutti i dati membro della classe base, può, per inizializzarli, richiamare il costruttore della classe base. Le modalità sono quelle del costrutto d’inizializzazione, già viste per l’inizializzazione dei dati costanti e degli oggetti membro. L’implementazione del costruttore assume quindi l’aspetto: Nome_Classe_Derivata :: Nome_Classe_Derivata (Argomenti_di_Inizializzazione_Classe_Base _e_Derivata) :Nome_Classe_Base (Parametri_Inizializzazione_Classe_Base) {…..Inizializzazione dati membro della classe derivata….} Nota: Se manca il richiamo al costruttore della classe base viene richiamata l’inizializzazione di default della classe base che deve esistere.
8 Ereditarietà Esempio: class Punto {//Classe base public:Punto (float =0., float=0.); void printPunto ( ); private: float x, y;}; ………………//Costruttore classe base: Punto :: Punto (float s, float t) { x= s; y= t;} …………….. class Segmento: public Punto {//Classe derivata public:Segmento (float x=0., float y=0., float x1=0., float y1=0.); private: float x1, y1; ………….. }; ……………... //Costruttore classe derivata: Segmento :: Segmento (float a, float b, float c, float d) :Punto (a, b) {……………………………...}
9 Ereditarietà Array di oggetti diversi E’ possibile costruire un array che contiene oggetti diversi se lo si dichiara del tipo della classe base da cui derivano le classi degli oggetti diversi. Casting Per mezzo dell’operatore di casting è possibile creare la versione temporanea di classe base di un oggetto di una classe derivata: i valori dei dati membro dell’oggetto di classe base saranno quelli al momento assunti dall’oggetto della classe derivata. L’operazione suddetta è la seguente: …… static_cast (Oggetto_Classe_Derivata)
10 Ereditarietà Puntatori a classi base e derivate Nell’ereditarietà di tipo public è sempre possibile assegnare un puntatore ad una classe derivata ad un puntatore ad una classe base con una operazione diretta: Puntatore_Classe_Base= & Nome_Oggetto_Classe_Derivata; Il puntatore alla classe base così inizializzato permette soltanto l’accesso ai membri relativi alla classe base dell’oggetto della classe derivata.
11 Ereditarietà Puntatori a classi base e derivate L’operazione inversa, ossia l’assegnazione di un puntatore alla classe derivata ad un puntatore alla classe base, può essere effettuata solo con una operazione di casting nel modo seguente: Puntatore_Classe_Derivata= static_cast (Puntatore_Classe_Base); Questa operazione da sola non permette però di trasferire al puntatore alla classe derivata la possibilità di accedere ai membri della classe derivata. Per ottenere ciò, prima del casting, il puntatore alla classe base deve essere stato già inizializzato con un puntatore alla classe derivata con una assegnazione come quella mostrata nella pagina precedente.
12 Ereditarietà class Punto {//Classe base public:Punto (float =0., float=0.); void printPunto ( ); private: float x, y;}; …………….. class Segmento: public Punto {//Classe derivata public:Segmento (float x=0., float y=0., float x1=0., float y1=0.); private: float x1, y1;………….. }; ……………… Segmento r(0.2, 0.5, 3.1, 3.5), *segPtr; Punto *punPtr; segPtr= &r; //segPtr accede i membri della classe derivata, //ma anche della classe base punPtr= &r;//punPtr accede solo i membri della classe base segPtr= static_cast (punPtr); //segPtr può accedere i membri sia della classe //base che della derivata
13 Ereditarietà Overriding di una funzione di una classe base in una classe derivata L’overriding (sovrascrittura) si può fare assegnando alla funzione di overriding nella classe derivata lo stesso nome e la stessa segnatura della funzione della classe base. La funzione di overriding della classe derivata deve comunque essere invocata ricorrendo ad un oggetto della classe derivata. La funzione sovrascritta può comunque ancora essere invocata dall’interno della classe derivata e, quindi, anche dalla funzione di overriding, ricorrendo all’operatore di risoluzione dello scope: Nome_Classe_Base :: Nome_Funzione_Sovrascritta (..Parametri…);
14 Ereditarietà Sequenza di esecuzione di costruttori e distruttori Dichiarando l’esistenza di un oggetto di una classe derivata contenente oggetti di altre classi e con una classe base contenente anch’essa oggetti di altre classi, l’ordine di esecuzione dei costruttori sarà il seguente: 1 - Costruttori degli oggetti contenuti nella classe base 2 - Costruttore della classe base 3 - Costruttori degli oggetti contenuti nella classe derivata 4 - Costruttore della classe derivata Al momento dell’uscita dallo scope dell’oggetto così creato, i distruttori vengono eseguiti nell’ordine inverso a quello sopra riportato.
15 Ereditarietà Relazioni tra oggetti e funzioni Le relazioni tra oggetti e funzioni finora incontrate possono essere inquadrate nella seguente casistica: Relazione is a has a uses a knows a Descrizione Ereditarietà di tipo public (es.: un oggetto della classe derivata “is a” un oggetto della classe base) Una classe contiene altre classi come membri (es.: la classe A “has a” un membro che è oggetto della classe B Uso da una classe di una funzione membro di un’altra classe (es.: la classe A “uses a” funzione della classe B) Un oggetto contiene un handle (puntatore o riferimento) di un altro oggetto.
16 Ereditarietà Ereditarietà multipla La dichiarazione dell’ereditarietà pubblica multipla è la seguente: class Nome_Classe_Derivata :public Nome_Prima_Classe_Base, Nome_Seconda_Classe_Base, ……..{ ………….. }; Il costruttore di una classe derivata pubblica con ereditarietà multipla assume quindi l’aspetto: Nome_Classe_Derivata :: Nome_Classe_Derivata (Argomenti_di_Inizializzazione_Classi_Base _e_Derivata) :Nome_Prima_Classe_Base (Parametri_Prima_Classe_Base), Nome_Seconda_Classe_Base (Parametri_Seconda_Classe_Base) ……………………………………………………………………… {…..Inizializzazione dati membro della classe derivata….}
17 Ereditarietà Ereditarietà multipla Nota: Possono sorgere problemi di ambiguità se le classi base hanno funzioni membro con lo stesso nome: in questo caso i problemi possono essere evitati invocando le funzioni con i nomi delle classi e l’operatore binario di risoluzione dello scope: Nome_Classe_Base :: Nome_Funzione (..Parametri…);