Upload
malvolio-franceschi
View
219
Download
0
Embed Size (px)
Citation preview
Ereditarietà Ereditarietà multiplamultipla
C++ method vs Twin-ObjectsC++ method vs Twin-ObjectsDaniela BriolaDaniela Briola
Orlin VelinovOrlin Velinov
Ereditarietà singolaEreditarietà singola
Ogni linguaggio Object Oriented Ogni linguaggio Object Oriented supporta il concetto di supporta il concetto di ereditarietàereditarietà
Si dice che una classe è Si dice che una classe è derivataderivata da un’altra quando ne estende le da un’altra quando ne estende le funzionalità grazie all’funzionalità grazie all’inheritanceinheritance
A
BA Nell’esempio la classe B è derivata Nell’esempio la classe B è derivata
dalla classe A.dalla classe A.
Ereditarietà multiplaEreditarietà multipla
Permette di comporre classi derivando Permette di comporre classi derivando da più classi base.da più classi base.
A B
CA B
Ereditarietà multiplaEreditarietà multipla
Possibilità di comporre Possibilità di comporre velocemente oggetti velocemente oggetti molto complessimolto complessi
Complicazione notevole Complicazione notevole del linguaggio che la del linguaggio che la implementaimplementa
Aggregare funzionalità Aggregare funzionalità differenti in un’unica differenti in un’unica classeclasse
Soluzione elegante e di Soluzione elegante e di grande utilitàgrande utilità
Scarsa efficienza anche Scarsa efficienza anche quando non viene usataquando non viene usata
Rischio elevato di Rischio elevato di “name clash”“name clash”
VantaggiVantaggi Svantaggi Svantaggi
Ereditarietà multipla:Ereditarietà multipla:il problema del diamanteil problema del diamante Se una classe eredita membri con lo Se una classe eredita membri con lo
stesso nome da più di un genitore, stesso nome da più di un genitore, avviene un conflittoavviene un conflitto
A
B1 B2
C
Ci sono due strategie possibili:Ci sono due strategie possibili:
gestire direttamente il grafo di gestire direttamente il grafo di ereditarietàereditarietà
trasformarlo in una catena lineare trasformarlo in una catena lineare (ereditarietà singola)(ereditarietà singola)
Ereditarietà multipla:Ereditarietà multipla:il problema del diamanteil problema del diamante
La semantica dei linguaggi orientati La semantica dei linguaggi orientati al grafo modella direttamente al grafo modella direttamente l’albero di derivazionel’albero di derivazione
Se un membro è definito solo dalla Se un membro è definito solo dalla classe A, è ereditato da B1 e da C classe A, è ereditato da B1 e da C senza errorisenza errori
Si deve prevedere il caso di Si deve prevedere il caso di ridefinizione dei metodi doppi da ridefinizione dei metodi doppi da parte di B2parte di B2
A
B1 B2
C
Ereditarietà multipla:Ereditarietà multipla:il problema del diamanteil problema del diamante
Soluzione LineareSoluzione Lineare:: La gerarchia di derivazione viene linearizzataLa gerarchia di derivazione viene linearizzata Si elimina l’invocazione multipla di metodi Si elimina l’invocazione multipla di metodi
della soluzione precedentedella soluzione precedente SvantaggiSvantaggi
Lo sviluppatore non è al corrente della Lo sviluppatore non è al corrente della gerarchia di sottotipazione implicitagerarchia di sottotipazione implicita
La selezione del metodo da utilizzare è a La selezione del metodo da utilizzare è a discrezione del compilatorediscrezione del compilatore
Problemi nel collegamento con il genitore Problemi nel collegamento con il genitore effettivoeffettivo
A
B2
B1
C
Ereditarietà multiplaEreditarietà multipla
Implementazione C++Implementazione C++
C++ Ereditarietà singolaC++ Ereditarietà singola
In C++ una In C++ una oggetto oggetto è una regione di memoria contiguaè una regione di memoria contigua
int aint a
class A {class A { int a;int a; void f(int i);void f(int i);};};
Nell’area di memoria riservata all’oggetto Nell’area di memoria riservata all’oggetto papa viene solo salvato un intero viene solo salvato un intero La funzione La funzione ff, essendo non-virtual (statica), è definita esternamente all’oggetto , essendo non-virtual (statica), è definita esternamente all’oggetto papa, quindi è come fosse una funzione/procedura normale., quindi è come fosse una funzione/procedura normale.
A* pa;A* pa;pa->f(2);pa->f(2);
int cint c
C++ Ereditarietà singolaC++ Ereditarietà singola
Gli oggetti composti (derivati) sono costruiti dal Gli oggetti composti (derivati) sono costruiti dal compilatore concatenando le aree di memoria.compilatore concatenando le aree di memoria.
class A { int a; void f(int); };class B : A { int b; void g(int); };class C : B { int c; void h(int); }; int bint b
int aint a
Se la classe definisce metodi Se la classe definisce metodi virtualvirtual entra in gioco la tabella delle funzioni entra in gioco la tabella delle funzioni (VMT): (VMT): ogni oggetto ha un puntatore alla VMT, che permette di identificare ogni oggetto ha un puntatore alla VMT, che permette di identificare la funzione effettivamente da usare.la funzione effettivamente da usare.
C++ PC++ Polimorfismoolimorfismo
Consente che gli oggetti assumano comportamenti Consente che gli oggetti assumano comportamenti differenti a seconda del contesto in cui operanodifferenti a seconda del contesto in cui operano
In particolare se In particolare se PtrPtr è un puntatore di tipo è un puntatore di tipo TT, allora , allora PtrPtr può puntarepuò puntare non solo a istanze di tipo non solo a istanze di tipo TT ma anche ma anche a a istanze di classi derivate da istanze di classi derivate da TT
T* Ptr = 0; // Puntatore nullo
/* ... */
Ptr = new Td; // Td è una classe derivata da T
Il polimorfismo è un concetto fondamentale della Il polimorfismo è un concetto fondamentale della programmazione OOprogrammazione OO
C++ PC++ Polimorfismo (2)olimorfismo (2)
C++ fa in modo che il corretto tipo dell’oggetto venga C++ fa in modo che il corretto tipo dell’oggetto venga determinato automaticamente alla chiamata della determinato automaticamente alla chiamata della funzionefunzione
In questo modo il linking della funzione viene rimandato In questo modo il linking della funzione viene rimandato a runtimea runtime (binding dinamico) (binding dinamico)
Per fare ciò bisogna dichiarare la funzione membro Per fare ciò bisogna dichiarare la funzione membro virtualvirtual
class T {class T {
public: virtual void Paint();public: virtual void Paint();
};};
C++ PC++ Polimorfismo:olimorfismo:ImplementazioneImplementazione
1.1. I metodi virtuali vengono ereditati allo stesso I metodi virtuali vengono ereditati allo stesso modo di quelli non virtualmodo di quelli non virtual, possono anch'essi essere , possono anch'essi essere sottoposti a overloading ed essere ridefinitisottoposti a overloading ed essere ridefiniti
2.2. non c'e` alcuna differenza eccetto chenon c'e` alcuna differenza eccetto che una loro una loro invocazione viene risolta a run-timeinvocazione viene risolta a run-time
3.3. In una classe con un metodo virtuale, In una classe con un metodo virtuale, il compilatore il compilatore associa alla classeassocia alla classe (non all'istanza) (non all'istanza) una tabella una tabella (VMT)(VMT) che contiene per ogni metodo virtuale l'indirizzo che contiene per ogni metodo virtuale l'indirizzo alla corrispondente funzionealla corrispondente funzione
4.4. Ogni istanza di quella classe conterrà poi al suo Ogni istanza di quella classe conterrà poi al suo interno un puntatore (VPTR) alla VMTinterno un puntatore (VPTR) alla VMT
C++ PC++ Polimorfismo:olimorfismo:OverheadOverhead
L'invocazione di un metodo virtuale e` piu` costosa di L'invocazione di un metodo virtuale e` piu` costosa di quella per una funzione membro ordinaria, tuttavia il quella per una funzione membro ordinaria, tuttavia il compilatore puo` evitare tale overhead risolvendo a compilatore puo` evitare tale overhead risolvendo a compile-time tutte quelle situazioni in cui il tipo e` compile-time tutte quelle situazioni in cui il tipo e` effettivamente noto effettivamente noto
Td Obj1;Td Obj1; T* Ptr = 0;T* Ptr = 0;
Obj1.Paint(); // Chiamata risolvibile staticamenteObj1.Paint(); // Chiamata risolvibile staticamente Ptr->Paint(); // Questa invece noPtr->Paint(); // Questa invece no
Polimorfismo Polimorfismo nell’ereditarietà multiplanell’ereditarietà multipla
class A { virtual void f(); };class A { virtual void f(); };class B { virtual void f(); virtual void g() };class B { virtual void f(); virtual void g() };class C: A, B { void f(); };class C: A, B { void f(); };
A* pa = new C;A* pa = new C; B* pb = new C;B* pb = new C; C* pc = new C;C* pc = new C;
pa->f();pa->f(); pb->f();pb->f(); pc->f();pc->f();
C eredita sia da A che da C eredita sia da A che da B, dunque l’assegnazione B, dunque l’assegnazione è correttaè corretta
Tutte tre le chiamate Tutte tre le chiamate invocano C::f()invocano C::f()
C++ Classi astratteC++ Classi astratte
Ereditarietà e polimorfismo possono essere combinati per Ereditarietà e polimorfismo possono essere combinati per realizzare classi il cui unico scopo è creare una interfaccia realizzare classi il cui unico scopo è creare una interfaccia comune a una gerarchia di classicomune a una gerarchia di classi
class TShape {class TShape { virtual void Paint() = 0;virtual void Paint() = 0; virtual void Erase() = 0;virtual void Erase() = 0; };};
Funzioni Funzioni virtuali purevirtuali pure
Una classe che possiede Una classe che possiede funzioni virtuali purefunzioni virtuali pure è detta è detta classe astratta e non è possibile istanziarlaclasse astratta e non è possibile istanziarla
Può essere utilizzata unicamente per derivare nuove Può essere utilizzata unicamente per derivare nuove classi forzandole a fornire determinati metodiclassi forzandole a fornire determinati metodi
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
AmbiguitàAmbiguitàAnalizziamo il caso in cui la Analizziamo il caso in cui la classe D derivi da B1 e B2classe D derivi da B1 e B2
class Base1 {class Base1 {
public:public:
void f();void f();
};};
class Base2 {class Base2 {
public:public:
void f();void f();
void f2();void f2();
};};
class Derived : Base1, Base2 {class Derived : Base1, Base2 {
// Non ridefinisce f()// Non ridefinisce f()
};};
B1 B2
DB1 B2
La classe La classe DerivedDerived eredita piu` eredita piu` volte gli stessi membri, in volte gli stessi membri, in particolare la funzione particolare la funzione f()f()
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
Ambiguità – soluzione esplicitaAmbiguità – soluzione esplicitaDerived x; Derived x; x.f()x.f() //Errore, è ambiguo! //Errore, è ambiguo!
N.B.: questo è un errore che appare solo a runtimeN.B.: questo è un errore che appare solo a runtime
Soluzione: Soluzione: Derived x; Derived x; x.B1::f()x.B1::f()
quanto detto vale anche per gli attributi;quanto detto vale anche per gli attributi; non è necessario che la stessa definizione si trovi in più non è necessario che la stessa definizione si trovi in più
classi basi dirette, è sufficiente che essa giunga alla classi basi dirette, è sufficiente che essa giunga alla
classe derivata attraverso due classi basi distinteclasse derivata attraverso due classi basi distinte il problema non si sarebbe posto se Derived avesse
ridefinito la funzione membro f().
C++ Ereditarietà multipla:C++ Ereditarietà multipla:Ambiguità - Ambiguità -
ImplementazioneImplementazione Come implementa il C++ una soluzione esplicita Come implementa il C++ una soluzione esplicita
attraverso qualificatore x.b1::f() ?attraverso qualificatore x.b1::f() ?
1.1. b1::f() si aspetta un puntatore b1* (che diventa il suo b1::f() si aspetta un puntatore b1* (che diventa il suo thisthis))
2.2. A runtime conosciamo però solo il puntatore della classe A runtime conosciamo però solo il puntatore della classe derivata “derived”derivata “derived”
3.3. Il compilatore aggiunge un opportuno Il compilatore aggiunge un opportuno deltadelta (memorizzato nella VMT) (memorizzato nella VMT) per raggiungere la parte per raggiungere la parte relativa a B1 in “derived”relativa a B1 in “derived”
4.4. In pratica, il compilatore trasforma una chiamata diretta in In pratica, il compilatore trasforma una chiamata diretta in una indiretta, sommando un offsetuna indiretta, sommando un offset
C++ Ereditarietà multipla:C++ Ereditarietà multipla:ImplementazioneImplementazione
C::f()C::f()
C::f()C::f()
B2::g()B2::g()
00
00
-delta(B2)-delta(B2)
Parte B1Parte B1
Parte B2Parte B2
Parte DParte D
Delta(B2)
VMT
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
AmbiguitàAmbiguità IIl problema dell'ambiguità può essere portato al caso l problema dell'ambiguità può essere portato al caso
estremo in cui una classe erediti più volte una stessa estremo in cui una classe erediti più volte una stessa classe baseclasse base
class Base { };class Base { };
class Derived1 : Base { };class Derived1 : Base { };
class Derived2 : Base { };class Derived2 : Base { };
class Derived3 : Derived1, Derived2 { };class Derived3 : Derived1, Derived2 { };
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
AmbiguitàAmbiguitàDerived3
Derived1 Derived2
Base Base
C++ Ereditarietà multipla:C++ Ereditarietà multipla:Ambiguità – ereditarietà Ambiguità – ereditarietà
virtualevirtuale Il C++ permette di risolvere il problema molto Il C++ permette di risolvere il problema molto
elegantemente con l’uso di elegantemente con l’uso di classi base virtualiclassi base virtuali
class Base { }; class Base { };
class Derived1 : class Derived1 : virtualvirtual Base { }; Base { };
class Derived2 : class Derived2 : virtualvirtual Base { }; Base { };
class Derived3 : Derived1, Derived2 { };class Derived3 : Derived1, Derived2 { };
Quando una classe eredita tramite la keyword Quando una classe eredita tramite la keyword virtualvirtual il il compilatore compilatore non copia il contenuto della classe base non copia il contenuto della classe base nella classe derivatanella classe derivata, ma , ma inserisce nella classe derivata inserisce nella classe derivata un puntatore ad un’unica istanza della classe base un puntatore ad un’unica istanza della classe base
C++ Ereditarietà multipla:C++ Ereditarietà multipla:Ambiguità – ereditarietà Ambiguità – ereditarietà
virtualevirtuale
Derived3
Derived1
Derived2
virtual
Base
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
Ambiguità – ridefinizioneAmbiguità – ridefinizione In alcuni casi l’ambiguità persiste. Supponiamo che una delle In alcuni casi l’ambiguità persiste. Supponiamo che una delle
classi intermedie ridefinisca una funzione membro della classi intermedie ridefinisca una funzione membro della classe base.classe base.
class Base {
public: void DoSomething();
};
class Derived1 : virtual Base {
public: void DoSomething();
};
class Derived2 : virtual Base {
public: void DoSomething();
};
class Derived3 : Derived1, Derived2 { };
Se Derived3 non ridefinisce DoSomething si crea ambiguità!
Quale metodo usare?
Il compilatore C++ segnala errore!
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
Ambiguità – ridefinizioneAmbiguità – ridefinizione La situazione è diversa se La situazione è diversa se solo una delle classi intermedie solo una delle classi intermedie
fa la ridefinizionefa la ridefinizioneclass Base {
public: void DoSomething(); };
class Derived1 : virtual Base {
public: void DoSomething(); };
class Derived2 : virtual Base {
/* … */ };
class Derived3 : Derived1, Derived2 { };
Solo Derived1 ridefinisce DoSomething (definizione dominante)
Il compilatore C++ non segnala errore!
class A : virtual L {...};
class B : virtual L {...};
class C : A , B {...};
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
Ambiguità – EsempioAmbiguità – EsempioLa “virtualità” di una classe non è una sua caratteristica, La “virtualità” di una classe non è una sua caratteristica, ma è data dall’essere dichiarata come tale nelle classi ma è data dall’essere dichiarata come tale nelle classi che la ereditano. Vediamo un esempio:che la ereditano. Vediamo un esempio:
In questo caso la In questo caso la classe C avrà un solo classe C avrà un solo riferimento ad un riferimento ad un oggetto di classe Loggetto di classe L
class D : L,C{...};In questo caso invece la In questo caso invece la classe D avrà due “sotto-classe D avrà due “sotto-oggetti” di tipo L, un virtuale oggetti” di tipo L, un virtuale e uno normalee uno normale
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
i costruttorii costruttori Le classi derivate normalmente chiamano Le classi derivate normalmente chiamano
implicitamente (o esplicitamente) i costruttori delle implicitamente (o esplicitamente) i costruttori delle classi baseclassi base
In caso di ereditarietà multipla con classe base virtuale In caso di ereditarietà multipla con classe base virtuale si pone il problema di decidere chi inizializza la classe si pone il problema di decidere chi inizializza la classe base (in quale ordine)base (in quale ordine)
In C++ le classi virtual sono inizializzate dalle classi In C++ le classi virtual sono inizializzate dalle classi massimamente derivatemassimamente derivate
In generale i costruttori sono eseguiti nell’ordine in cui In generale i costruttori sono eseguiti nell’ordine in cui compaiono nella dichiarazione eccetto quelli delle classi compaiono nella dichiarazione eccetto quelli delle classi virtual, eseguiti primavirtual, eseguiti prima
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
i distruttorii distruttori Stesso discorso per i distruttori, ma in ordine Stesso discorso per i distruttori, ma in ordine
contrariocontrario
Il compilatore C++ si preoccupa di Il compilatore C++ si preoccupa di distruggere le classi virtual una sola voltadistruggere le classi virtual una sola volta , , anche se vengono ereditate molteplici volteanche se vengono ereditate molteplici volte
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
Problemi di efficienzaProblemi di efficienzaL’ereditarietà multipla comporta alcuni costi in termini di L’ereditarietà multipla comporta alcuni costi in termini di
efficienza:efficienza:1.1.Sottrazione di una costante per ogni accesso ai membri Sottrazione di una costante per ogni accesso ai membri
delle classi basedelle classi base2.2.Un word per funzione in ogni VMT (per il delta)Un word per funzione in ogni VMT (per il delta)3.3.Un riferimento in memoria ed una sottrazione per ogni Un riferimento in memoria ed una sottrazione per ogni
chiamata a funzione virtualechiamata a funzione virtuale4.4.Un riferimento in memoria ed una sottrazione per ogni Un riferimento in memoria ed una sottrazione per ogni
accesso ai membri di una classe base virtualeaccesso ai membri di una classe base virtuale
La 1 e la 4 sono penalizzanti solo se l’ereditarietà multipla è La 1 e la 4 sono penalizzanti solo se l’ereditarietà multipla è effettivamente usata. La 2 e la 3 sempreeffettivamente usata. La 2 e la 3 sempre
Il metodo qui presentato offre due modalità di estendere il Il metodo qui presentato offre due modalità di estendere il “name space” di una classe:“name space” di una classe:
classe baseclasse base classe base virtualeclasse base virtuale
Comunque, le regole per gestire questi due tipi di classi Comunque, le regole per gestire questi due tipi di classi sono indipendenti dal tipo di classe effettivamente usata, sono indipendenti dal tipo di classe effettivamente usata, inoltre:inoltre:
le ambiguità sono illegalile ambiguità sono illegali le regole per la gestione dei vari membri sono le stesse che le regole per la gestione dei vari membri sono le stesse che
con ereditarietà singolacon ereditarietà singola le regole di visibilità ed inizializzazione sono le stesse le regole di visibilità ed inizializzazione sono le stesse
dell’ereditarietà singoladell’ereditarietà singola Violazioni di queste regole sono segnalate a compile-timeViolazioni di queste regole sono segnalate a compile-time
C++ Ereditarietà C++ Ereditarietà multipla:multipla:
ConsiderazioniConsiderazioni
C++ Ereditarietà multipla:C++ Ereditarietà multipla:ConclusioniConclusioni
L’ereditarietà multipla, in una forma pratica da usare, è relativamente semplice da aggiungere al C++
Per essere implementata richiede piccolissime modifiche alla sintassi e si adatta naturalmente alla già preesistente struttura
L’implementazione è efficiente sia in tempo che in spazio, dal momento che, soprattutto su calcolatori moderni, semplici operazioni di somma o sottrazione o un campo in più nella VMT non costituiscono un overhead pesante
La compatibilità con il C non è compromessa, e neppure la portabilità
Ereditarietà multiplaEreditarietà multipla
Implementazione modelloImplementazione modello“Twin Objects”“Twin Objects”
(J.Templ)(J.Templ)
Twin ObjectsTwin Objects
Sono un modo di realizzare l’ereditarietà multipla Sono un modo di realizzare l’ereditarietà multipla usando l’ereditarietà singolausando l’ereditarietà singola
Possono essere usati per implementare l’ereditarietà Possono essere usati per implementare l’ereditarietà multipla in linguaggi che non la supportano, ad multipla in linguaggi che non la supportano, ad esempio in Javaesempio in Java
Aiutano a risolvere problemi tipici dell’ereditarietà Aiutano a risolvere problemi tipici dell’ereditarietà multipla quali l’ambiguità dei nomimultipla quali l’ambiguità dei nomi
Twin Objects - modelloTwin Objects - modello
CA e CB sono chiamati twins (gemelli)CA e CB sono chiamati twins (gemelli) Sono sempre generati assieme e legati dai Sono sempre generati assieme e legati dai
puntatori T1 e T2puntatori T1 e T2
Multiple InheritanceMultiple Inheritance Twin objectsTwin objects
A BB
C
A BB
CBCBCACA
CCT1T1
T2T2
Twin Objects - modelloTwin Objects - modello
A BB
CBCB
CACA
EE
CECE
Se la classe C eredita da Se la classe C eredita da nn classi base, ci saranno classi base, ci saranno nn twins da gestiretwins da gestire
CC
Twin Objects - modelloTwin Objects - modello
Se dobbiamo inserire nella nostra classe attributi o metodi Se dobbiamo inserire nella nostra classe attributi o metodi aggiuntivi (non definiti nelle classi basi) abbiamo due metodi:aggiuntivi (non definiti nelle classi basi) abbiamo due metodi:
P1 P2P2
C2C2CCT1T1
T2T2
li mettiamo in uno dei due li mettiamo in uno dei due twin twin (ad es. in C1, che (ad es. in C1, che chiamiamo C); questo è chiamiamo C); questo è l’approccio seguito l’approccio seguito normalmentenormalmente
creiamo una classe creiamo una classe aggiuntiva C in cui li aggiuntiva C in cui li inseriamoinseriamo
Twin Objects - Twin Objects - ereditarietàereditarietà
P1 P2P2
C2C2CCT1T1
T2T2
D2D2D1D1T1T1
T2T2
C2C2
P1 P2P2
CCT1T1
T2T2
DDNo
D non eredita da C2!D non eredita da C2!
Twin Objects - Twin Objects - CollaborazioneCollaborazione
Ogni classe figlio è responsabile per la comunicazione Ogni classe figlio è responsabile per la comunicazione con il suo padre e si occupa di inoltrare i messaggi alle con il suo padre e si occupa di inoltrare i messaggi alle classi gemelleclassi gemelle
I client referenziano uno dei figli direttamente, e tutti gli I client referenziano uno dei figli direttamente, e tutti gli altri tramite i puntatori a twin (la ‘T’ negli esempi)altri tramite i puntatori a twin (la ‘T’ negli esempi)
I client che necessitano di comunicare con uno dei I client che necessitano di comunicare con uno dei Padri, lo fanno attraverso la rispettiva classe-figlioPadri, lo fanno attraverso la rispettiva classe-figlio
Twin Objects - Twin Objects - ImplemenazioneImplemenazione
AstrazioneAstrazione: le classi gemelle devono cooperare : le classi gemelle devono cooperare strettamente tra loro, permettendo di accedere ai loro strettamente tra loro, permettendo di accedere ai loro membri privati (visibilità package in Java). Il tutto deve membri privati (visibilità package in Java). Il tutto deve apparire come un unico oggetto dall’esterno.apparire come un unico oggetto dall’esterno.
EfficienzaEfficienza: l’uso di twin objects sostituisce le relazioni : l’uso di twin objects sostituisce le relazioni per ereditarietà con relazioni per composizione. Ciò per ereditarietà con relazioni per composizione. Ciò comporta la necessità di inoltrare messaggi e quindi comporta la necessità di inoltrare messaggi e quindi minore efficienza, ma poiché l’ereditarietà multipla è in minore efficienza, ma poiché l’ereditarietà multipla è in genere più lenta, non si notano differenze sostanziali.genere più lenta, non si notano differenze sostanziali.
Raggruppare i Twin in un unico blocco contiguo per Raggruppare i Twin in un unico blocco contiguo per velocizzare l’allocazione dell’oggetto che li velocizzare l’allocazione dell’oggetto che li usausanecessità di utilizzare puntatori nel blocco per necessità di utilizzare puntatori nel blocco per collegare i twincollegare i twin
Twin Objects - Twin Objects - OttimizzazioniOttimizzazioni
sostituire il puntatore con un offset relativo all’inizio sostituire il puntatore con un offset relativo all’inizio del twindel twin
se l’allocazione degli oggetti client e dei twin è resa se l’allocazione degli oggetti client e dei twin è resa uguale per ogni istanza, l’offset è una costante uguale per ogni istanza, l’offset è una costante memorizzabile a partememorizzabile a parte
le VMT possono essere memorizzate le VMT possono essere memorizzate contiguamente in un bloccocontiguamente in un blocco
Twin Objects - EsempioTwin Objects - Esempio
Java non consente l’ereditarietà multiplaJava non consente l’ereditarietà multipla, tuttavia in , tuttavia in alcuni casi serve poter mettere assieme oggetti di alcuni casi serve poter mettere assieme oggetti di natura diversa, ad esempio implementando applet che natura diversa, ad esempio implementando applet che reagiscono alle azioni del mousereagiscono alle azioni del mouse
Costruendo un Costruendo un appletapplet, serve poter ereditare da un , serve poter ereditare da un generico generico AppletApplet a cui verrà ridefinito il metodo a cui verrà ridefinito il metodo .Paint().Paint() e da una classe e da una classe StdMouseListenerStdMouseListener di cui verranno di cui verranno ridefiniti i metodi ridefiniti i metodi mousePressed(), mouseClicked() mousePressed(), mouseClicked() ee mouseReleased()mouseReleased()
Lo schema che segue riassume la struttura del nostro Lo schema che segue riassume la struttura del nostro oggetto composto MyApplet + MyAppletListeneroggetto composto MyApplet + MyAppletListener
Twin Objects – EsempioTwin Objects – Esempio
Applet
resize()paint()…
StdMouseListenerStdMouseListener
mousePressed()mouseClicked()mouseReleased()
paint()paint()CC T1T1
T2T2
MyAppletMyApplet
mousePressed()mouseClicked()mouseReleased()
MyAppletListenerMyAppletListener
Twin Objects - EsempioTwin Objects - Esempio
class Applet {class Applet {
public void paint();public void paint();
public void resize();public void resize();
……
}}
class StdMouseListener {class StdMouseListener {public void mousePressed();public void mousePressed();public void mouseClicked();public void mouseClicked();public void mouseReleased();public void mouseReleased();……
}} Queste sono le definizioni delle due classi base di cui desideriamo Queste sono le definizioni delle due classi base di cui desideriamo
fare ereditarietà multipla attraverso l’uso del modello Twin Objectsfare ereditarietà multipla attraverso l’uso del modello Twin Objects
Twin Objects - EsempioTwin Objects - Esempio
class MyApplet extends Applet {class MyApplet extends Applet {
MyAppletListener listener; /* il Twin */MyAppletListener listener; /* il Twin */
public void paint() { /* ridefinisco */ }public void paint() { /* ridefinisco */ }
……
}}class MyAppletListener extends StdMouseListener {class MyAppletListener extends StdMouseListener {
MyApplet applet; /* il Twin */MyApplet applet; /* il Twin */public void mousePressed () { /* ridefinisco */ }public void mousePressed () { /* ridefinisco */ }public void mouseClicked () { /* ridefinisco */ }public void mouseClicked () { /* ridefinisco */ }public void mouseReleased () { /* ridefinisco */ }public void mouseReleased () { /* ridefinisco */ }……
}} Ogni “twin” eredita il proprio parent ridefinendone i metodi Ogni “twin” eredita il proprio parent ridefinendone i metodi
opportuni e si occupa di comunicare con il proprio fratello.opportuni e si occupa di comunicare con il proprio fratello.
Twin Objects - EsempioTwin Objects - Esempio
Layout in memoria:Layout in memoria:
MyAppletMyApplet
AppletApplet
MyStdMouseListenerMyStdMouseListener
StdMouseListenerStdMouseListener
twinstwins
Come evidente, si tratta di oggetti completamente separati a livello di Come evidente, si tratta di oggetti completamente separati a livello di memoria. Il link tra le classi è fatto a livello di applicazione.memoria. Il link tra le classi è fatto a livello di applicazione.
C++ - Contro esempioC++ - Contro esempio
class Applet {class Applet {virtual void paint();virtual void paint();virtual void resize();virtual void resize();……
}}class StdMouseListener {class StdMouseListener {
virtual void mousePressed();virtual void mousePressed();virtual void mouseClicked();virtual void mouseClicked();virtual void mouseReleased();virtual void mouseReleased();……
}} Come per l’esempio Twin Objects, abbiamo due classi base iniziali Come per l’esempio Twin Objects, abbiamo due classi base iniziali
da cui vogliamo fare ereditarietà multipla...da cui vogliamo fare ereditarietà multipla... ““virtualvirtual” indica che le funzioni sono di tipo latebinding (come ” indica che le funzioni sono di tipo latebinding (come
nell’es. Java) e non statiche.nell’es. Java) e non statiche.
C++ - Contro esempioC++ - Contro esempio
class MyApplet : Applet, StdMouseListener {class MyApplet : Applet, StdMouseListener {
void paint() { /* ridefinisco */ }void paint() { /* ridefinisco */ }
void mousePressed() { /* ridefinisco */ }void mousePressed() { /* ridefinisco */ }
void mouseClicked() { /* ridefinisco */ }void mouseClicked() { /* ridefinisco */ }
void mouseReleased() { /* ridefinisco */ }void mouseReleased() { /* ridefinisco */ }
……
}} Il C++ ci consente di avere un’unica classe MyApplett che Il C++ ci consente di avere un’unica classe MyApplett che
eredita contemporaneamente da Applet e StdMouseListener.eredita contemporaneamente da Applet e StdMouseListener. Ridefinisco i metodi secondo le esigenze; per fortuna non ci sono Ridefinisco i metodi secondo le esigenze; per fortuna non ci sono
name clashes quindi non ci preoccupiamo di usare qualificatori name clashes quindi non ci preoccupiamo di usare qualificatori esplicitiespliciti
A livello implementativo, il compilatore traduce una chiamata del A livello implementativo, il compilatore traduce una chiamata del tipo *obj->paint() in una chiamata indiretta sommando un delta tipo *obj->paint() in una chiamata indiretta sommando un delta riferito alla classe parent e memorizzato nella VMTriferito alla classe parent e memorizzato nella VMT
C++ contro esempioC++ contro esempio
Myapp::paint()Myapp::paint()
Myapp::paint()Myapp::paint()
00
-delta(StdML)-delta(StdML)
Parte Parte AppletApplet
Parte Parte StdmouseStdmouselistenerlistener
Parte Parte MyappletMyapplet
metodi ridefiniti da metodi ridefiniti da MyAppletMyApplet
VMTVMT
delta(StdML)delta(StdML)
L’implementazione dell’ereditarietà multipla del C+L’implementazione dell’ereditarietà multipla del C++ porta ad un overhead anche dell’ereditarietà + porta ad un overhead anche dell’ereditarietà singolasingola
Il codice deve essere riaggiustato per cambiare il Il codice deve essere riaggiustato per cambiare il “self”“self”
L’ereditarietà multipla in se stessa non è né molto L’ereditarietà multipla in se stessa non è né molto utile né veramente necessaria dal momento che utile né veramente necessaria dal momento che può essere facilmente simulatapuò essere facilmente simulata
Il vantaggio che offre è puramente sintatticoIl vantaggio che offre è puramente sintattico Il suo costo non è giustificato dalle opportunità che Il suo costo non è giustificato dalle opportunità che
offreoffre
Considerazioni di Templ Considerazioni di Templ sull’ereditarietà multiplasull’ereditarietà multipla
ConclusioniConclusioni L’ereditarietà multipla è uno strumento potente che L’ereditarietà multipla è uno strumento potente che
consente di affrontare problemi complessi con eleganzaconsente di affrontare problemi complessi con eleganza La sua implementazione nativa può generare un lieve La sua implementazione nativa può generare un lieve
decadimento del performances anche quando non viene decadimento del performances anche quando non viene usatausata (vedi C++) (vedi C++)
Gestire un linguaggio con ereditarietà multipla può Gestire un linguaggio con ereditarietà multipla può divenire comunque complesso e poco chiaro (sia per il divenire comunque complesso e poco chiaro (sia per il programmatore che per l’implementatore)programmatore che per l’implementatore)
I linguaggi moderni tendono ad evitarla (es. Java), I linguaggi moderni tendono ad evitarla (es. Java), adoperando tecniche altrettanto efficaci come i Twin adoperando tecniche altrettanto efficaci come i Twin Objects, Objects, senza overhead e complicazioni, dal momento senza overhead e complicazioni, dal momento che i vantaggi, sebbene ci siano, forse non giustificano che i vantaggi, sebbene ci siano, forse non giustificano le necessarie modifiche dei linguaggi OO già esistentile necessarie modifiche dei linguaggi OO già esistenti
RiferimentiRiferimenti
““Twin – A Design Pattern for Modeling Multiple Twin – A Design Pattern for Modeling Multiple Inheritance”Inheritance” J. J. TemplTempl
““Multiple Inheritance for C++” Multiple Inheritance for C++” Bjarne StroustrupBjarne Stroustrup
Manuale del C++Manuale del C++ Bjarne Stroustrup Bjarne Stroustrup