Ereditarietà multipla: differenze tra le versioni

Da Wikipedia, l'enciclopedia libera.
Vai alla navigazione Vai alla ricerca
Contenuto cancellato Contenuto aggiunto
Orfanizzo redirect
Problema del diamante: Migliorato la pagina
Etichetta: Editor wikitesto 2017
 
(3 versioni intermedie di 2 utenti non mostrate)
Riga 1: Riga 1:
{{F|programmazione|febbraio 2013}}
{{F|programmazione|febbraio 2013}}
Alcuni [[linguaggi di programmazione]] permettono di utilizzare l<nowiki>'</nowiki>'''ereditarietà multipla''', in cui una [[Classe (informatica)|classe]] può ereditare funzionalità e caratteristiche da più di una [[Classe base (informatica)|classe base]]. Questa tecnica si contrappone all<nowiki>'</nowiki>'''ereditarietà singola''', in cui una classe può ereditare da una, e solo una, classe base.
Alcuni [[linguaggi di programmazione]] permettono di utilizzare l{{'}}'''ereditarietà multipla''', in cui una [[Classe (informatica)|classe]] può ereditare funzionalità e caratteristiche da più di una [[Classe base (informatica)|classe base]]. Questa tecnica si contrappone all'[[Ereditarietà (informatica)|ereditarietà singola]], in cui una classe può ereditare da una, e solo una, classe base.


Nella [[programmazione orientata agli oggetti]], l'[[Ereditarietà (informatica)|ereditarietà]] descrive una relazione gerarchica fra due classi, nella quale una (quella che eredita) è chiamata "classe derivata" (o "classe figlia", o "sottotipo"), e l'altra (quella da cui si eredita) è chiamata "classe genitrice" (o "classe base", o "superclasse"). Le funzionalità ereditate dalla "figlia" permettono agli oggetti istanziati su di essa di condividere le funzionalità della "genitrice", cioè della classe base.
Nella [[programmazione orientata agli oggetti]], l'[[Ereditarietà (informatica)|ereditarietà]] descrive una relazione gerarchica fra due classi, nella quale una (quella che eredita) è chiamata "classe derivata" (o "classe figlia", o "sottotipo"), e l'altra (quella da cui si eredita) è chiamata "classe genitrice" (o "classe base", o "superclasse"). Le funzionalità ereditate dalla "figlia" permettono agli oggetti istanziati su di essa di condividere le funzionalità della "genitrice", cioè della classe base.
Riga 7: Riga 7:


<code>Mammifero</code>
<code>Mammifero</code>
dotata di funzionalità come mangiare, respirare, riprodursi, ecc. Possiamo definire un'altra classe:

dotata di funzionalità come ''mangiare, respirare, riprodursi, ecc.''. Possiamo definire un'altra classe:


<code>Topo</code>
<code>Topo</code>
che eredita tutte le funzionalità di <code>Mammifero</code> senza doverle ridefinire, in quanto, essendo il topo un mammifero, ne condivide tutte le caratteristiche. A queste funzionalità la nuova classe ne aggiunge altre, possedute dai "topi" e non comuni a tutti i mammiferi, come, ad esempio raccogliere spazzatura. Oltre che avere le caratteristiche comuni a tutti i mammiferi, tuttavia, i topi condividono altre caratteristiche e funzionalità con altre "classi" di oggetti, come, ad esempio:

che eredita tutte le funzionalità di <code>Mammifero</code> '''senza''' doverle ridefinire, in quanto, essendo il topo un mammifero, ne condivide tutte le caratteristiche. A queste funzionalità la nuova classe ne aggiunge altre, possedute dai "topi" e '''non''' comuni a tutti i mammiferi, come, ad esempio ''raccogliere spazzatura''. Oltre che avere le caratteristiche comuni a tutti i mammiferi, tuttavia, i topi condividono altre caratteristiche e funzionalità con altre "classi" di oggetti, come, ad esempio:


<code>Personaggi di Ratatouille</code> oppure <code>Vettori di malattie mortali</code>
<code>Personaggi di Ratatouille</code> oppure <code>Vettori di malattie mortali</code>
Quindi è molto comodo, per la classe <code>Topo</code>, poter ereditare anche le caratteristiche e le funzionalità di queste classi, senza doverle ridefinire.


== Problema del diamante ==
Quindi è molto comodo, per la classe <code>Topo</code>, poter ereditare '''anche''' le caratteristiche e le funzionalità di queste classi, senza doverle ridefinire.

==Problema del diamante==
[[File:Diamond inheritance.svg|thumb|Diagramma del "problema del diamante"]]
[[File:Diamond inheritance.svg|thumb|Diagramma del "problema del diamante"]]
L'ereditarietà multipla può essere causa, in alcuni contesti, di qualche confusione, tanto che alcuni ritengono che gli inconvenienti siano maggiori dei benefici.
L'ereditarietà multipla può essere causa, in alcuni contesti, di qualche confusione, tanto che alcuni ritengono che gli inconvenienti siano maggiori dei benefici.
Riga 24: Riga 21:
Una possibile causa di ambiguità è la seguente: se due classi B e C [[ereditarietà (informatica)|ereditano]] dalla classe A e la classe D eredita sia da B che da C, se un [[metodo (programmazione)|metodo]] in D chiama un metodo definito in A, da quale classe viene ereditato?
Una possibile causa di ambiguità è la seguente: se due classi B e C [[ereditarietà (informatica)|ereditano]] dalla classe A e la classe D eredita sia da B che da C, se un [[metodo (programmazione)|metodo]] in D chiama un metodo definito in A, da quale classe viene ereditato?


Tale ambiguità prende il nome di '''problema del diamante''' (in [[lingua inglese|inglese]] '''diamond problem'''), a causa della forma del diagramma di ereditarietà delle classi, simile ad un diamante.
Tale ambiguità prende il nome di problema del diamante ({{Inglese|diamond problem}}), a causa della forma del diagramma di ereditarietà delle classi, simile ad un diamante.


Differenti linguaggi di programmazione hanno risolto quest'inconveniente in modi diversi.
Differenti linguaggi di programmazione hanno risolto quest'inconveniente in modi diversi.


==Implementazione in alcuni linguaggi==
== Implementazione in alcuni linguaggi ==
===[[Java (linguaggio di programmazione)|Java]] e [[Microsoft .NET|.NET]]===
=== Java e .NET ===
In [[Java (linguaggio di programmazione)|Java]] si è adottato questo compromesso: una classe può ereditare le [[Interfaccia (informatica)|interfacce]] da più di una classe base cioè esporre all'esterno gli stessi metodi delle interfacce delle classi base ma può ereditare i dati ed i metodi effettivi da una sola classe base. Anche i linguaggi della piattaforma [[Microsoft .NET]], come [[C sharp|C#]] e [[Visual Basic .NET|Visual Basic]] utilizzano lo stesso approccio.


Dalla versione Java 8, tuttavia, si sono introdotti i modificatori "default" che se associati a metodi di interfacce permettono (al contrario dei metodi senza questo modificatore) non solo di dichiarare ma anche di implementare in modo differente gli stessi metodi nelle interfacce. È comunque d'obbligo l'[[override]] dei metodi nelle sottoclassi che implementano entrambe le interfacce, ma tramite un'istruzione ben definita si può invocare un preciso metodo implementato di una specifica interfaccia genitrice. Così nel caso del problema del diamante la classe D effettua l'override del metodo in comune a B e C definito in A e invoca lo specifico metodo di B o C.
In Java si è adottato questo compromesso: una classe può ereditare le [[Interfaccia (informatica)|interfacce]] da più di una classe base - cioè esporre all'esterno gli stessi metodi delle interfacce delle classi base - ma può ereditare i dati ed i metodi effettivi da una sola classe base. Anche i linguaggi della piattaforma Microsoft .NET, come [[C sharp|C#]] e [[Visual Basic .NET|Visual Basic]] utilizzano lo stesso approccio.


=== C++ ===
Dalla versione Java 8, tuttavia, si sono introdotti i modificatori "default" che se associati a metodi di interfacce permettono (al contrario dei metodi senza questo modificatore) non solo di dichiarare ma anche di implementare in modo differente gli stessi metodi nelle interfacce. È comunque d'obbligo l'override dei metodi nelle sottoclassi che implementano entrambe le interfacce, ma tramite un'istruzione ben definita si può invocare un preciso metodo implementato di una specifica interfaccia genitrice. Così nel caso del problema del diamante la classe D effettua l'override del metodo in comune a B e C definito in A e invoca lo specifico metodo di B o C.
[[C++]] per default segue ogni percorso di ereditarietà separatamente, quindi l'oggetto D contiene due separati oggetti di A e gli usi dei membri di A devono essere opportunamente qualificati. Se l'ereditarietà da A a B e l'ereditarietà da A a C sono entrambe segnate come "virtual" ("class B : virtual A"), C++ si prende particolare cura di creare solo un oggetto A, l'utilizzo dei membri di A funziona correttamente. Se l'[[ereditarietà virtuale]] e l'ereditarietà non virtuale sono mischiate, esiste solo un A virtuale e un A non virtuale per ogni percorso di ereditarietà non virtuale a A.


===[[C++]]===
=== Eiffel ===
In [[Eiffel (linguaggio di programmazione)|Eiffel]] le classi derivate adattano le funzionalità ereditate rinominandole o definendo anticipatamente le regole per risolvere le ambiguità e decidere quale versione applicare (quella ereditata o quella della classe base).
C++ per default segue ogni percorso di ereditarietà separatamente, quindi l'oggetto D contiene due separati oggetti di A e gli usi dei membri di A devono essere opportunamente qualificati. Se l'ereditarietà da A a B e l'ereditarietà da A a C sono entrambe segnate come "virtual" ("class B : virtual A"), C++ si prende particolare cura di creare solo un oggetto A, l'utilizzo dei membri di A funziona correttamente. Se l'[[ereditarietà virtuale]] e l'ereditarietà non virtuale sono mischiate, esiste solo un A virtuale e un A non virtuale per ogni percorso di ereditarietà non virtuale a A.

===[[Eiffel (linguaggio)|Eiffel]]===

In Eiffel le classi derivate ''adattano'' le funzionalità ereditate rinominandole o definendo anticipatamente le regole per risolvere le ambiguità e decidere quale ''versione'' applicare (quella ereditata o quella della classe base).

===[[REALbasic (linguaggio)|REALbasic]]===


=== REALbasic ===
Analogamente a Java anche in REALbasic si possono ereditare metodi e dati da una sola classe base, e, in più, sono previsti metodi aggiuntivi per "estendere" le funzionalità di una classe senza dover ricorrere all'ereditarietà.
Analogamente a Java anche in REALbasic si possono ereditare metodi e dati da una sola classe base, e, in più, sono previsti metodi aggiuntivi per "estendere" le funzionalità di una classe senza dover ricorrere all'ereditarietà.


===[[Perl]]===
=== Perl ===
In [[Perl]] le classi da cui ereditare sono disposte in una lista ordinata, e viene poi chiamato il primo metodo trovato in ordine gerarchico di profondità della prima classe della lista, e così di seguito per le classi successive e per le loro rispettive classi base.

In Perl le classi da cui ereditare sono disposte in una lista ordinata, e viene poi chiamato il primo metodo trovato in ordine gerarchico di profondità della prima classe della lista, e così di seguito per le classi successive e per le loro rispettive classi base.

===[[Python]]===

Python supporta una forma di ereditarietà multipla, e viene chiamato un metodo attraverso la regola prima-in-profondità, da-sinistra-a-destra. Perciò, se un attributo non viene trovato nella classe derivata, viene cercato nella prima classe base, poi (ricorsivamente) nelle classi base di quest'ultima e, solo se non vi è stato trovato, viene ricercato nella seconda classe base della classe derivata, e così via.


=== Python ===
[[Python]] supporta una forma di ereditarietà multipla, e viene chiamato un metodo attraverso la regola prima-in-profondità, da-sinistra-a-destra. Perciò, se un attributo non viene trovato nella classe derivata, viene cercato nella prima classe base, poi (ricorsivamente) nelle classi base di quest'ultima e, solo se non vi è stato trovato, viene ricercato nella seconda classe base della classe derivata, e così via.
<!--
<!--

[[CLOS]] allows for complete programmer control of method combination, and if that's not enough the [[meta-object protocol]] gives the programmer a means to ''modify'' the inheritance, [[method dispatch]], [[class instantiation]], and other internal mechanisms without affecting the stability of the system.
[[CLOS]] allows for complete programmer control of method combination, and if that's not enough the [[meta-object protocol]] gives the programmer a means to ''modify'' the inheritance, [[method dispatch]], [[class instantiation]], and other internal mechanisms without affecting the stability of the system.

-->
-->


== Esempio ==
== Esempio ==
Ecco un esempio di ereditarietà multipla in [[C++]]:
Ecco un esempio di ereditarietà multipla in C++:


<syntaxhighlight lang="c++" line="1">
<pre>
class A
class A
{
{
Riga 90: Riga 79:
void set_k(int n) {k=n;}
void set_k(int n) {k=n;}
};
};
</syntaxhighlight>
</pre>


In questo caso, <code>C</code> erediterà sia da <code>A</code> che da <code>B</code>, ed avrà quindi le variabili d'istanza <code>i</code> e <code>j</code> e le funzioni <code>get_i</code>, <code>set_i</code>, <code>get_j</code> e <code>set_j</code>, oltre che a <code>k</code> ed a <code>get_k</code> ed a <code>set_k</code>.
In questo caso, <code>C</code> erediterà sia da <code>A</code> che da <code>B</code>, ed avrà quindi le variabili d'istanza <code>i</code> e <code>j</code> e le funzioni <code>get_i</code>, <code>set_i</code>, <code>get_j</code> e <code>set_j</code>, oltre che a <code>k</code> ed a <code>get_k</code> ed a <code>set_k</code>.


==Voci correlate==
== Voci correlate ==

* [[Classe (informatica)]]
* [[Classe (informatica)]]
* [[Classe astratta]]
* [[Classe astratta]]
Riga 106: Riga 94:


== Collegamenti esterni ==
== Collegamenti esterni ==
* {{FOLDOC|multiple inheritance|multiple inheritance}}
* {{cita web|1=http://builder.com.com/5100-6373-5030734.html|2=Articolo di Jonathan Lurie di Builder.Com sull'ereditarietà multipla nei linguaggi della piattaforma .NET|lingua=en|accesso=21 ottobre 2005|urlarchivio=https://web.archive.org/web/20060112044813/http://builder.com.com/5100-6373-5030734.html|dataarchivio=12 gennaio 2006|urlmorto=sì}}
* {{cita web|1=http://builder.com.com/5100-6373-5030734.html|2=Articolo di Jonathan Lurie di Builder.Com sull'ereditarietà multipla nei linguaggi della piattaforma .NET|lingua=en|accesso=21 ottobre 2005|urlarchivio=https://web.archive.org/web/20060112044813/http://builder.com.com/5100-6373-5030734.html|dataarchivio=12 gennaio 2006|urlmorto=sì}}
* [http://books.google.it/books?id=xDVdBaO6fC8C&pg=PA394&lpg=PA394&dq=problema+del+diamante&source=bl&ots=Gfd3E5uLDj&sig=dUZwyL3QAPWm3jnY4MNjR1nBhq8&hl=it&sa=X&ei=GpJ7UbPNJYGG4AShzoHAAw&ved=0CFYQ6AEwBg#v=onepage&q=problema%20del%20diamante&f=false Il problema del diamante su ''UML e ingegneria del software: dalla teoria alla pratica''] (da Google Libri)
* [http://books.google.it/books?id=xDVdBaO6fC8C&pg=PA394&lpg=PA394&dq=problema+del+diamante&source=bl&ots=Gfd3E5uLDj&sig=dUZwyL3QAPWm3jnY4MNjR1nBhq8&hl=it&sa=X&ei=GpJ7UbPNJYGG4AShzoHAAw&ved=0CFYQ6AEwBg#v=onepage&q=problema%20del%20diamante&f=false Il problema del diamante su ''UML e ingegneria del software: dalla teoria alla pratica''] (da Google Libri)

{{Portale|Informatica}}
{{Portale|Informatica}}



Versione attuale delle 13:57, 16 lug 2024

Alcuni linguaggi di programmazione permettono di utilizzare l'ereditarietà multipla, in cui una classe può ereditare funzionalità e caratteristiche da più di una classe base. Questa tecnica si contrappone all'ereditarietà singola, in cui una classe può ereditare da una, e solo una, classe base.

Nella programmazione orientata agli oggetti, l'ereditarietà descrive una relazione gerarchica fra due classi, nella quale una (quella che eredita) è chiamata "classe derivata" (o "classe figlia", o "sottotipo"), e l'altra (quella da cui si eredita) è chiamata "classe genitrice" (o "classe base", o "superclasse"). Le funzionalità ereditate dalla "figlia" permettono agli oggetti istanziati su di essa di condividere le funzionalità della "genitrice", cioè della classe base.

Per esempio: supponiamo di aver creato una classe:

Mammifero dotata di funzionalità come mangiare, respirare, riprodursi, ecc. Possiamo definire un'altra classe:

Topo che eredita tutte le funzionalità di Mammifero senza doverle ridefinire, in quanto, essendo il topo un mammifero, ne condivide tutte le caratteristiche. A queste funzionalità la nuova classe ne aggiunge altre, possedute dai "topi" e non comuni a tutti i mammiferi, come, ad esempio raccogliere spazzatura. Oltre che avere le caratteristiche comuni a tutti i mammiferi, tuttavia, i topi condividono altre caratteristiche e funzionalità con altre "classi" di oggetti, come, ad esempio:

Personaggi di Ratatouille oppure Vettori di malattie mortali Quindi è molto comodo, per la classe Topo, poter ereditare anche le caratteristiche e le funzionalità di queste classi, senza doverle ridefinire.

Problema del diamante

[modifica | modifica wikitesto]
Diagramma del "problema del diamante"

L'ereditarietà multipla può essere causa, in alcuni contesti, di qualche confusione, tanto che alcuni ritengono che gli inconvenienti siano maggiori dei benefici.

Una possibile causa di ambiguità è la seguente: se due classi B e C ereditano dalla classe A e la classe D eredita sia da B che da C, se un metodo in D chiama un metodo definito in A, da quale classe viene ereditato?

Tale ambiguità prende il nome di problema del diamante (in inglese diamond problem), a causa della forma del diagramma di ereditarietà delle classi, simile ad un diamante.

Differenti linguaggi di programmazione hanno risolto quest'inconveniente in modi diversi.

Implementazione in alcuni linguaggi

[modifica | modifica wikitesto]

In Java si è adottato questo compromesso: una classe può ereditare le interfacce da più di una classe base – cioè esporre all'esterno gli stessi metodi delle interfacce delle classi base – ma può ereditare i dati ed i metodi effettivi da una sola classe base. Anche i linguaggi della piattaforma Microsoft .NET, come C# e Visual Basic utilizzano lo stesso approccio.

Dalla versione Java 8, tuttavia, si sono introdotti i modificatori "default" che se associati a metodi di interfacce permettono (al contrario dei metodi senza questo modificatore) non solo di dichiarare ma anche di implementare in modo differente gli stessi metodi nelle interfacce. È comunque d'obbligo l'override dei metodi nelle sottoclassi che implementano entrambe le interfacce, ma tramite un'istruzione ben definita si può invocare un preciso metodo implementato di una specifica interfaccia genitrice. Così nel caso del problema del diamante la classe D effettua l'override del metodo in comune a B e C definito in A e invoca lo specifico metodo di B o C.

C++ per default segue ogni percorso di ereditarietà separatamente, quindi l'oggetto D contiene due separati oggetti di A e gli usi dei membri di A devono essere opportunamente qualificati. Se l'ereditarietà da A a B e l'ereditarietà da A a C sono entrambe segnate come "virtual" ("class B : virtual A"), C++ si prende particolare cura di creare solo un oggetto A, l'utilizzo dei membri di A funziona correttamente. Se l'ereditarietà virtuale e l'ereditarietà non virtuale sono mischiate, esiste solo un A virtuale e un A non virtuale per ogni percorso di ereditarietà non virtuale a A.

In Eiffel le classi derivate adattano le funzionalità ereditate rinominandole o definendo anticipatamente le regole per risolvere le ambiguità e decidere quale versione applicare (quella ereditata o quella della classe base).

Analogamente a Java anche in REALbasic si possono ereditare metodi e dati da una sola classe base, e, in più, sono previsti metodi aggiuntivi per "estendere" le funzionalità di una classe senza dover ricorrere all'ereditarietà.

In Perl le classi da cui ereditare sono disposte in una lista ordinata, e viene poi chiamato il primo metodo trovato in ordine gerarchico di profondità della prima classe della lista, e così di seguito per le classi successive e per le loro rispettive classi base.

Python supporta una forma di ereditarietà multipla, e viene chiamato un metodo attraverso la regola prima-in-profondità, da-sinistra-a-destra. Perciò, se un attributo non viene trovato nella classe derivata, viene cercato nella prima classe base, poi (ricorsivamente) nelle classi base di quest'ultima e, solo se non vi è stato trovato, viene ricercato nella seconda classe base della classe derivata, e così via.

Ecco un esempio di ereditarietà multipla in C++:

class A
{
 protected:
  int i;
 public:
  int get_i() {return i;}
  void set_i(int n) {i=n;}
};

class B
{
 protected:
  int j;
 public:
  int get_j() {return j;}
  void set_j(int n) {j=n;}
};

class C : public A, public B
{
 protected:
  int k;
 public:
  int get_k() {return k;}
  void set_k(int n) {k=n;}
};

In questo caso, C erediterà sia da A che da B, ed avrà quindi le variabili d'istanza i e j e le funzioni get_i, set_i, get_j e set_j, oltre che a k ed a get_k ed a set_k.

Voci correlate

[modifica | modifica wikitesto]

Collegamenti esterni

[modifica | modifica wikitesto]
  Portale Informatica: accedi alle voci di Wikipedia che trattano di Informatica