Newsgroup: it.comp.lang.c++
Titolo: [OT] Multiple Inheritance VS single Inheritance

Salve a tutti,
sto iniziando a studiare il c++ seriamente e e insieme ad un collega abbiamo
avuto una accesa conversazione dove lui mi faceva notare che java è un vero
linguaggio ad oggetti e non supporta la derivazione multipla in quanto per
Sun è meglio evitare che le persone che non sanno programmare possano
sbagliare come in c++ usando la derivazione multipla (ci sono molti più
problemi).
Il tutto è iniziato da una mia osservazione difensiva verso il c++:
il modello OO amette la possibilità di derivazione multipla che può essere
necessaria in rari casi, java invece è più limitato e prevede solo la
derivazione singola...  Dopo avermi risposto dicendo che la derivazione
multipla non serve in OO Io ho replicato dicendo che secondo me Sun non ha
consentito la derivazione multipla sostituendola con le interfacce in quanto
la JVM sarebbe stata molto più complicata da realizzare... e lui mi ha
risposto che non centra niente...
Poichè sono curioso di sapere come stanno le cose potrei avere delle
delucidazioni da qualcuno più esperto di noi?
Ringrazio vivamente per l'eventuale attenzione!

 problemi).
La preoccupazione principale di Sun non è certo quella di evitare che le 
persone che non sappiano programmare possano sbagliarsi, anche perché 
non è privando un linguaggio dell'ereditarietà multipla che si risolve 
il problema ;-)  Il punto però è che la MI ha sempre sollevato delle 
diatribe persino tra guru (vedi Stanley Lippman, Tom Cargill, Bjarne 
Stroustrup, Brad Cox o Grady Booch) e la sua reale utilità è sempre 
messa in discussione.  In realtà, la MI, così come implementata in C++, 
nella pratica serve molto meno di quanto uno possa pensare e non rende 
di certo il C++ un linguaggio superiore ad altri, come il modello di 
Java non rende quest'ultimo superiore al C++.
Sono semplicemente questioni di design.
Sicuramente il supporto alla MI è più complicato da realizzare e 
introduce diversi problemi.  In realtà, il modello a interfacce 
sostituisce benissimo la MI, nella pratica di tutti i giorni.
Riguardo la MI, lo stesso Stanley Lippman ha affermato: "I have never 
used multiple inheritance, although, as I stated earlier, I have 
supported one implementation, and implemented a second. Tom Cargill has 
a long history of writing against the need for MI. Grady Booch has 
called it a parachute – something that might save one’s life in an 
emergency, but not something one uses every day [...]
Multiple inheritance is not a cornerstone of the C++ language. It was 
introduced into the language in Release 2.0, in part because Brad Cox, 
the inventor of Objective C, said it couldn’t be done. Stroustrup has 
subsequently said that he wished he had done templates before MI".
Quindi, come vedi, Java non è "più limitato" come affermi, perché 
l'utilità della MI è realmente discutibile.  Adotta semplicemente il 
modello che è utilizzato nella maggior parte dei casi e in molti altri 
linguaggi (vedi Delphi e C++ per .NET) e che viene citato dallo stesso 
Lippman nel suo "C++ Primer":
"Ther is no language-imposed limit on the number of base classes from 
which a class can be derived. In practice, two base classes seems to be 
the most common, with on base class often representing a public abstract 
interface and the second base class providing implementation[...]"
[Stanley Lippman, Josée Lajoie - "C++ Primer", 3^ ed. pag. 970]
Just my 2 cents ;-)

 [Stanley Lippman, Josée Lajoie - "C++ Primer", 3^ ed. pag. 970]
Ma dal 1998 il modo di intendere e di usare il C++ e' cambiato
drasticamente.
Ciao,
Nicola

 [Stanley Lippman, Josée Lajoie - "C++ Primer", 3^ ed. pag. 970]
Dal punto concettuale è discutibile ciò che dice Lippman! Se una delle due
classi serve per l'implementazione non dovrebbe essere esposta come base
pubblica.
Certo oramai è impossibile introdurre un meccanismo di ereditarietà multipla
in linguaggi come Java o nel .NET. Di default anche le derivazioni singole
dovrebbero essere "virtual" (nel senso del C++) pena il non poter usare
l'ereditarietà multipla con la libreria base.
Tanto per intenderci, provate ad assegnare un Context Attribute in C# da una
classe derivata da Form. Chi ci riesce direttamente (senza adapter et
simila) mi fornisca una soluzione. Con l'ereditarietà multipla già avrei
risolto.
Fulvio
--------------------------------
Inviato via 
http://arianna.libero.it/usenet/

 drasticamente.
Sicuramente, ne prendo nota ;-) A proposito, ma non si diceva che 
sarebbe venuto fuori uno standar nuovo ogni 5 anni ?
A questo punto, però, sarebbe molto interessante se potessi fare un 
esempio (o rimandarmi a qualche link) in cui la MI si rivela un mezzo 
davvero utile e schiacciante rispetto ad altri.  Te lo chiedo perché 
sarebbe molto istruttivo per me stesso, innanzitutto ;-)

 Dal punto concettuale è discutibile ciò che dice Lippman!
Considerando che è uno di quelli che ha inventato il C++ ;-)
Magari, come dice Nicola, dal 1998 ad oggi la pensa diversamente, 
spulcerò sul suo blog ;-)

 ha
scritto:



 > spulcerò sul suo blog ;-)
Lippman non ha inventato l'oo. So (non per esperienza personale) che in
eiffel la MI è usatissima al punto che è stata introdotta anche nel
compilatore .net
Scusa per il ritardo nella risposta ma avevo il pc rotto
Fulvio


Lippman non ha inventato l'oo.
Il discorso mi sembrava riferito al C++ nello specifico e Lippman ha
contribuito a inventarlo eccome.


http://www.eptacom.net/pubblicazioni/pub_it/ered.html
Avevo letto questo articolo (come i molti altri pubblicati sul sito di
Carlo Pescio), ma non mi sembra che aggiunga molto al discorso
iniziale.  In realtà, io rimango convinto della mia idea, anche
motivata dalle varie letture (es.:
http://www.artima.com/intv/abcs.html
), ovvero che la single
inheritance + interface model sia un buon compromesso per non
impappinarsi con la MI.  Nessuno finora mi ha fatto un esempio
significativo per farmi cambiare idea ;-)

 ha
scritto:

Certo, ma qui si parla di ereditarietà multipla e dove può avere senso, il
discorso non è puramente rilegato al C++. Non fraintendermi, non dico che
Lippman sia uno stupido (anzi :-) il fatto è che spesso anche gli esperti
"cadono" se si confrontano con persone che magari non sono "popolari" ma
forse ne sanno anche qualcosina in più. Emblematico è il caso
dell'intervista di C. Pescio ad alex stepanov (disponibile sul sito eptacom)
oppure il fatto che G.Booch confonda (anzi ritenga la stessa cosa)
information hiding e incapsulazione (si veda "abstraction, encapsulation and
information hiding" di E.Berard sul sito 
www.toa.com
). Come al solito è
sempre meglio vedere cosa ne dice l'altra campana.


 ha
scritto:

Vedi, io non dico che non sia sufficiente nella maggior parte dei casi, il
fatto è che avere l'ereditarietà multipla non tocca nessuno se non la usa.
Nei casi potrebbe essere comoda (come ad esempio applicare un context
attribute del .net ad una classe che non deriva da ContextBoundObject (che
non è una interface) rende i context attribute non utilizzabili in una
miriade di casi utili. L'assenza dell'ereditarietà multipla rende necessaria
una progettazione delle libreire molto più accurata se non si vuole che
risultino inusabili in casi utili!
P.S. Non conosco Java a sufficenza per trovare qualcosa a riguardo!


                    Il 26 Nov 2005, 16:13, 
fulvio_es...@libero.it
 (Fulvio Esposito) ha
scritto:

Un post interessante sull'argomento:
http://www.eptacom.net/blog/2005/07/multiple-inheritance.html


una progettazione delle libreire molto più accurata [...]
A me sembra che sia l'esatto contrario.  La MI è un elemento di
complessità in più, in un linguaggio, quindi usarla male può
significare realmente fare danni.  In ogni caso, qui non si sta
parlando di "assenza" di ereditarietà multipla, perché sia Java, che
Delphi (tanto per fare due esempi) prevedono la possibilità di
ereditare da più fonti.  Quando si parla di "limitazione" di un
linguaggio rispetto all'altro, non ha senso affermare: "Il linguaggio
X è limitato rispetto al linguaggio Y, perché gli manca la feature Z".
Bisogna invece dimostrare che nel linguaggio X è impossibile fare cose
che nel linguaggio Y si fanno mediante la feature Z.  Mi aspetto che
tu mi fornisca un esempio di ciò che si può fare con la MI e che sia
impossibile fare con il modello SI+interface.

oppure il fatto che G.Booch confonda (anzi ritenga la stessa cosa)
Sinceramente, l'OOP è un campo talmente aperto alle interpretazioni e
ai modi di vedere - come la programmazione in generale, peraltro - che
il fatto che G. Booch stia "fraintendendo" o "confondendo" qualcosa lo
prendo cum grano salis.  In realtà, da persone così esperte sul campo
mi aspetto diverse visioni, tutte filosoficamente valide.

 ha
scritto:

Sai benissimo che non è possibile trovarlo. I linguaggi sono
computazionalmente equivalenti (tesi di Church), non posso trovare una
singola cosa che possa essere fatta in un linguaggio che non sia fattibile
per un altro. Se però andiamo a vedere tempi modi e quant'altro le cose
cambiano. Leggiti il post sul blog di carlo pescio che ti ho segnalato
nell'altro messaggio.


cambiano. 
Ok, allora andiamoli a vedere, è questo che chiedo dall'inizio.  Cerco
un esempio concreto legato a ciò di cui stiamo parlando, in cui si
dimostri che Delphi o Java o qualsiasi altro linguaggio con SI sia più
"limitato" del C++.

 impossibile fare con il modello SI+interface.
Sono d'accordo con te.


Non lo troverai. L'eredità multipla è zucchero sintattico, alla fin
fine. Serve a velocizzare le cose, ma non è niente di insostituibile. E'
utile in quanto permette di esprimere i concetti che si hanno in mente
in un modo abbastanza naturale, ma dopotutto:
# ------------- C++ --------
class House {
public:
	House();
	virtual ~House();
	virtual void cookDinner();
	virtual void sleepInto( Room& r );
	virtual void closeDoor(int n) = 0;
protected:
	std::vector<Door> _doors;
	std::vector<std::pair<Room,float> > _rooms;
};
class Car : public Vehicle {
public:
	Car();
	virtual ~Car();
	virtual void startEngine();
	virtual double checkGasLevel() const;
	virtual void closeDoor(int n) = 0;
protected:
	std::vector<Door> _doors;
	double _gas_level;
};
class Camper : public Car, public House {
public:
	Camper();
	virtual ~Camper();
	virtual void closeDoor(int n);
};
# --------------------------
In Java è più lungo, ma anche pulito, tutto sommato:
# ----------- Java ---------
// Lo tengo più corto, come esempio...
interface HouseI {
	public void cookDinner();
	public void sleepInto(Room r);
	public void closeDoor(int n);
}
interface CarI {
	public void startEngine();
	public double checkGasLevel();
	public void closeDoor(int n);
}
class House implements HouseI {
	// ... code
}
class Car extends Vehicle implements CarI {
	// ... code
}
public class Camper implements HouseI, CarI {
	private class _House extends House { /* ... */ }
	private class _Car extends Car { /* ... */ }
	private _House _house; // altrimenti, puoi usare
        private _Car   _car;   // inner class statiche...
	public void cookDinner() {
		_house.cookDinner();
	}
	// ...
	public double checkGasLevel() {
		return _car.checkGasLevel();
	}
	// ...
	public void closeDoor(int n) {
		_car.closeDoor(n); // nessuna ambiguità
	}
}
# --------------------------
... e almeno sai dove sbattere la testa quando hai variabili membro con
lo stesso nome in due classi "base" differenti.
Come vedi, ho digitato di più, ma il risultato non è male: nessuna
ambiguità, massima estendibilità...
Se proprio vuoi denigrare Java per qualcosa, ce n'è a piene mani senza
tirare in ballo la MI. Io per esempio ce l'ho a morte con gli
iteratori... ma questa è un'altra storia.
Ad ogni modo, imho la MI è utile, permette di espimere in modo naturale
delle idee astratte, ma se ne può fare a meno -- a me piace, ma va usata
con parsimonia.
Ciao,
-- 
Matteo Settenvini
FSF Associated Member
Email : 
mat...@member.fsf.org
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d--(-) s+:- a-- C++ UL+++
P?>++ L+++>$ E+>+++ W+++ N++ o?
w--- O- M++ PS++ PE- Y+>++
PGP+++ t+ 5 X- R tv-- b+++ DI+
D++ G++ e h+ r-- y?
------END GEEK CODE BLOCK------

Se proprio vuoi denigrare Java per qualcosa[...]
Ahem... io non denigro Java, forse ti confondi con qualcun'altro ;-)

 "limitato" del C++.
Temple ha mostrato come sia possibile implementare l'ereditarietà multipla 
utilizzando la singola. Quindi è impossibile trovare esempi che mostrino 
qualcosa che sia impossibile da fare con la sola ereditarietà singola.
Pertanto la richiesta corretta dovrebbe essere di mostrare esempi
concreti in cui l'ereditarietà multipla permette di scrivere codice più
semplice e più chiaro. 
Personalmente ritengo l'esempio di Matteo già uno di questi esempi, ma
forse è questione di "gusti" ed abitudini. Comunque penso si possa fare
molto meglio, ma non conosco abbastanza a fondo Java per poterlo fare
io.
In ogni caso, come lo stesso Temple ha mostrato, una corretta
implementazione dell'ereditarietà multipla non ha nessun influenza nel
linguaggio se non utilizzata. Pertanto trovo comunque una limitazione
(per quanto piccola) non averla, anche se mi dovesse essere utile una 
sola volta nella vita.
Ciao.
-- 
BSD is for UNIX lovers -- linux is for Windows haters.

sola volta nella vita.
Io ho imparato, nel tempo, ad abbandonare i principi "filosofici", a
favore di quelli più pragmatici ;-)

 favore di quelli piů pragmatici ;-)
Se mi serve la uso, se non mi serve non la uso.
Piů pragmatico di cosě...


Più pragmatico di così...
Non ci siamo capiti: mi riferivo a quanto esprimevi nel tuo messaggio
precedente in cui dicevi di considerare comunque limitato un
linguaggio che non disponga della MI alla stregua del C++.
In realtà si scopre, scavando sotto sotto, che un linguaggio come Java
o come Delphi, che supportano il modello SI + interfaces coprono
benissimo il 100% dei casi in cui la MI del modello C++ risulta utile.
Accetto il punto di vista di chi dice che i linguaggi di un certo tipo
hanno le stesse potenzialità e che, alla fine, il discorso si traduce
nel capire se in C++ si possono fare cose, mediante la MI, più
comodamente e in modo migliore di quanto è possibile in altri
linguaggi.  Tuttavia, una reale dimostrazione in questo senso la devo
ancora ricevere.  Della reale utilità della MI, poi, si è discusso
spesso, persino tra guru di OOP e/o C++ e non c'è un'opinione comune
sulla sua reale utilità.  Se Carlo Pescio la pensa in un modo, Grady
Booch la pensa in un altro, e così via.

 Booch la pensa in un altro, e così via.
Certamente a riguardo esistono moltissime opinioni differenti. Quello
che però io penso mi pare, senza falsa modestia, piuttosto ragionevole. 
Detto in parole molto semplici:
se c'è la possibilità di avere la MI a costo zero (cioé senza *nessun*
impatto per chi non la usa) perché non mettercela? Se non serve a nulla,
come alcuni pensano, nessuno la userà e non darà fastidio a nessuno.
Ma se c'è anche un solo caso in cui è utile il non avercela messa avrà
portato alla scrittura di codice peggiore.
A me pare che che il non volerla sia dovuto alla volontà di semplificare
il compilatore piuttosto che il linguaggio...


 come alcuni pensano, nessuno la userà e non darà fastidio a nessuno.
Mi pare piuttosto illogico: il supporto alla MI, nella 
progettazione di un linguaggio di programmazione, è un'attività 
impegnativa.  Impegnarsi per introdurre una caratteristica che non 
serve a nulla (sempre ammesso che sia così) è poco intelligente.
Meglio concentrarsi su altre cose, non trovi?.  Lo stesso 
Stroustrup ha ammesso che avrebbe voluto dedicarsi prima e meglio 
ai template. In ogni caso, lo ripeto, non si sta confrontando un 
linguaggio che non possiede la MI con uno che la possiede, ma due 
linguaggi di cui uno possiede una MI per così dire "pura" e 
l'altro il modello a interfacce.  E' una MI implementata diversamente.

 ha
scritto:

Vedo che quando parlo non leggi tutto quello che dico. Leggi il post che ti
ho indicato nel blog di Carlo Pescio e vedrai perchè si è lamentato anche
lui dell'assenza della MI e del perchè le libreire vadano progettate più
attentamente per non essere inutilizzabili in contesti tutto sommato utili.
Lo so che si parla del .Net, ma ti assicura che le differenze da Java non
sono moltissime!


 l'altro il modello a interfacce. E' una MI implementata diversamente.
Ogni features di un linguaggio richiede valutazioni impegnative, anche
l'ingresso di una features sintattica deve essere valuta per l'impatto che
dà all'intero progetto. Ma implementare la MI al giorno d'oggi non dovrebbe
essere così complicato dato che sono sgorgati fiumi di inchiostro
sull'argomento. Stroustrup ha certamente ammesso che avrebbe voluto
dedicarsi prima e al meglio ai template, ma l'uso che se ne fa oggi, in
assenza di ereditarietà multipla sarebbe molto più limitato (si pensi a
boost o Loki).
Comunque sia, chiunque si sia trovato ad utilizzare framework o librerie che
basano la maggior parte dei meccanismi di estendibilità lato utente
sull'ereditarietà si è trovato molto più comodo ad avere la MI.


 
Ciao

 con parsimonia.
Meglio ancora per certi versi sono i mixins. Tipicamente mi danno la
possibilità di fare cose con *molto* meno codice.
Pensa per esempio a dover definire un'interfaccia "comparable".
In C++ te la cavi benissimo. Fai una classe con un metodo astratto che
una volta implementato restituirà 0,1,-1 a seconda del confronto, e poi
definisci i vari operatori in funzione di quell'unico operatore (qui
penso a Ruby che ha il <=> ).
Poi definisci gli operatori di confronto in funzione di questa
"compare".
Questo non è un mixin, ma mi permette comunque di scrivere una quantità
limitatissima di codice.
Se ho un buon numero di classi che devono essere comparate, io le faccio
ereditare anche (ereditarietà multipla) da questa Comparable e sono a
posto.
In Ruby posso usare il mixin vero e proprio. In Python posso fare come
in C++ (a parte per il fatto della classe astratta, cosa che
sintatticamente non esiste).
In Java di solito si implementa solo l'interfaccia "Comparable", con il
suo metodo compareTo. Se voglio dei le, ge, lt, gt li devo scrivere
tutti a mano. Ora il limite di questo esempio è che siccome Java non ha
gli operatori non ci sono grossi vantaggi visivi ad usare ge invece di
compareTo.
Purtroppo non mi viene in mente un esempio migliore.
Comunque tipicamente quando la situazione è implementare un certo numero
di metodi la cui semantica dipende da pochi altri metodi (magari uno
solo), con una classe astratta ce la si cava egregiamente. Idem con i
mixin.
Con l'ereditarietà singola invece si deve scrivere più codice. Niente di
trascendente, per carità, probabilmente basta un po' di copia e incolla.
È comunque un esempio in cui l'ereditarietà multipla fa comodo (e pure
uno in cui non è considerata cattiva pratica di programmazione).
-- 
non vincono, non vinceranno, non hanno domani
la forza è nel puntello impugnato da oneste fortissime mani

 l'altro il modello a interfacce.  E' una MI implementata diversamente.
Infatti il codice di Matteo confrontava proprio questi due aspetti.
In ogni caso anche in C++ l'uso della MI solo con classi base astratte
(interfacce) elimina buona parte delle problematiche che la MI può 
generare.


 è una limitazione.
Se il metodo alternativo è altrettanto semplice e chiaro sì, altrimenti
è una limitazione. Che non significa però che il linguaggio sia
limitato.


 
Ciao

Vedo che quando parlo non leggi tutto quello che dico. 
Quando uno "parla" è difficile "leggere" ;-)
Fulvio, ti invito ad usare un tono diverso, io "leggo quello che
dici", ma non è detto che "leggendo tutto quello che dici", io debba
essere necessariamente d'accordo con te.  Semplicemente, non
condivido.

Comunque sia, chiunque si sia trovato[...]
Dei "chiunque si sia trovato" mi fido sempre molto poco, specie se
detti da una persona sola.  O hai statistiche su *tutti* gli
sviluppatori del mondo, oppure, sempre se mi permetti, prendo ciò che
dici con delle enormi molle ;-)

eredita in modo virtale, altrimenti no.
E come fai a progettare una classe se non sai, a priori, che potrà
essere ereditata in un contesto di ereditarietà multipla?

dire che il linguaggio è limitato sono due cose completamente diverse.
Chiamasi rigirare la frittata ;-)
Leggi il post originale...

 Leggi il post originale...
In effetti non l'avevo mai letto perché il mio newsreader (od il mio
newsserver) si era perso l'inizio del thread (i primi 3-4 post),
altrimenti ti avrei contestato fin da subito questa affermazione: 
"Sicuramente il supporto alla MI è più complicato da realizzare e
introduce diversi problemi.", come ho poi fatto in seguito.
Comunque, chiamalo pure "rigirare la frittata" se vuoi, ma dire che un
linguaggio è più limitato (sotto un certo aspetto), come ha fatto l'OP è
ancora una cosa diversa dal dire che un linguaggio è limitato.
D'altra parte non credo tu abbia la possibilità di contestare se io ti
dico che Java è più limitato del C++ (in generale)...


 essere ereditata in un contesto di ereditarietą multipla?
Queste sono scelte che deve fare il progettista valutando il "problema"
nel contesto.


 Questo a mio parere, ovviamente.
Per lo stesso motivo per il quale non programmiamo tutti e solo
macchine di Turing: perche' la disponibilita' di particolari feature
nei linguaggi di programmazione ci permette di scrivere codice
migliore, per una qualche definizione di migliore.
Nel caso dell'ereditarieta' multipla di implementazione il vantaggio
sta nell'evitare di scrivere troppo codice di delega se non addirittura
duplicato inutilmente. In generale trovo che quando individuo una
classe base astratta che definisce l'interfaccia comune ad un gruppo di
classi concrete in genere esiste anche un'implementazione tipica o piu'
frequente di altre di questa interfaccia, che voglio raccogliere a
fattor comune. Con l'eredita' singola in molti linguaggi posso
scrivere:
class A { public: int f() { return 42; } };
class Concreta : public A {};
oppure, per migliorare la separazione tra interfaccia e
implementazione:
class InterfacciaA { public: virtual int f() = 0; };
class ImplementazioneA : public InterfacciaA { public: int f() { return
42; } };
class Concreta : public ImplementazioneA {};
Ma se concreta deve anche implementare l'interfaccia B? Senza eredita'
multipla di implementazione devo scrivere:
class InterfacciaB { public: virtual int g() = 0; };
class ImplementazioneB { public: int g() { return 27; };
class Concreta : public ImplementazioneA, public InterfacciaB {
  public:
    int g() { return b.g(); }
  private:
    ImplementazioneB b;
};
L'implementazione di g va ripetuta in tutte le classi che implementano
B come seconda interfaccia. Io la trovo una limitazione arbitraria e
forzata, che diventa un vero e proprio incubo, nonche' causa di errori
e sviste se si devono gestire gerarchie di interfacce piu' complesse.
Nota che con la seconda soluzione che ho adottato per A io posso
limitarmi ad esporre esternamente la classe InterfacciaA ed evitare i
problemi di accoppiamento con la sua implementazione.
Ciao,
Nicola
P.S. Spero di avere almeno in parte risposto al tuo quesito dell'inizio
del thread.


Se un aspetto di questo rilievo non e' noto in sede di progettazione
probabilmente l'ereditarieta', anche singola, non e' una soluzione
adatta al problema. L'elevato grado di accoppiamento che accompagna
l'ereditarieta' suggerisce di utilizzarla solo tra classi
effettivamente affini.
In generale una classe dovrebbe essere usata come base solo se e' stata
effettivamente pensata come derivabile, altrimenti meglio ricorrere
alla delega. Due classi dovrebbero avere una base comune solo se
esistono dei contesti nei quali le loro istanze devono essere
considerate come omogenee.
Ciao,
Nicola

 è una limitazione.
Certo che lo e' se ti costa piu' fatica. Altrimenti saremmo ancora
tutti contenti di spostare levette per rappresentare gli zeri e gli
uni!
Ciao,
Nicola


E sbagliate tutt'e due, dal momento che nessuno ha ancora dimostrato
che esistono linguaggi piu' "potenti" delle macchine di Turing.
Il problema e' l'espressivita' di un linguaggio, ovvero con che agio vi
permette di fare le cose che volete fare.
Ciao,
Nicola Musatti

 ha
scritto:

Scusami se sono sembrato saccente. Il fatto è che nel post di Carlo Pescio
sul suo blog si capisce un po' meglio quello che dico, ma dalla tua risposta
sembrava non l'avessi letto. Quella è una situazione in cui la MI aiuta.
Lungi da me a dire che Java è più limitato, non l'ho mai pensato, e poi
viene utilizzato con successo in molti progetti. Il fatto è che una feature
non dovrebbe essere tagliata per ragioni implementative e poi tacciata come
"pericolosa". Vedo a volte persone che commettono errori banali da cui è
difficile uscirne che riguardavano cose banali. Inserire l'ereditarietà
multipla e lasciare la libertà di poter decidere mi sembra una buona cosa,
se poi uno non vuole usarla fatti suoi. Ma se io ed altri trovano comodo
averla...


 Considerando che è uno di quelli che ha inventato il C++ ;-)
Io sapevo che era stato Bjarne Stroustrup da solo, sbaglio?
Ciao

 impossibile fare con il modello SI+interface.
Posso provarci io?
Fai tutto anche con la macchina di Turing.
Ma fammi agevolmente questo.
Comunque per esempio:
Ho 3 tabelle sul database:
tab1) codutente descrizione
tab2) codlavoro descrizione
tab3) codutente codlavoro
Per mie pippe mentali desidero.
Class tab1 con membro statico codutente protected
Class tab2 con membro statico codlavoro protected
Class tab3 con membro che deriva da tab1 e tab2
Ora quando in tab3 setto il campo codutente in automatico, visto che
condivido il dato viene settato anche codutente di tab1, visto che abbiamo
nelle classi altri metodi il metodo sniff() di tab1 scopre che codutente e'
cambiato chiama il database ed aggiorna il valore di descrizione.
Ora gentilmente come potrei fare altrettanto agevolmente tutto questo in 
modello SI+interface?
Avrei un'altro punto di vista visto che sono C++ MI amante.
Se puoi rimaniamo nel pseudocodice, mi pare piu' facile.
Sono riuscito a spiegare la struttura?
Ciao

 qualcosa che sia impossibile da fare con la sola ereditarietà singola.
Certo, si puo' fare tutto anche con la macchina di Turing o scrivere un
sistema operativo in Assebly.
La riusabilita'?
-- 
Ciao

 ancora ricevere.
Ti puo' aiutare il mio post sopra?
:D
-- 
Ciao

 che modo quel codice Java sia più complesso dell'equivalente C++
E' piu' verboso proprio perche gli manca la MI
-- 
Ciao

 essere ereditata in un contesto di ereditarietą multipla?
E' quella la potenza della MI e la capacita' del progettista altrimenti
basta leggersi un libro di C++ o di java....
:D
Ciao

 del thread
Si molto bene, w la MI
:D
Ciao

 gran vantaggio.
Dipende, pensa quando parli in Italiano usi una parola che richiude un
significato, esempio:
giuntare e' una parola complicata ma ti evita di dire:
Congiungere, unire cucendo o altrimenti: g. due nastri, due pezzi di spago,
due spezzoni di pellicola, due teli, ecc.
Come vedi giuntare e' piu' complicato ma e' meno verboso.
Java e' bello perche' piu' portabile ma e' senza dubbio un linguaggio piu'
verboso.
Ciao

 
Ciao

 SI+interface nn fa.....
Certo nessuno e' riuscito a dimostrare che la macchina di Turing non riesca
a fare quello che fa la MI.
:)

 
Dalle tue parole quanto ho detto non ti dovrebbe risultare tanto "ovvio"....


 "ovvio"....
Concordo va meglio?
:D
Ciao

 Ciao
Ciao


Dovresti lasciarlo dire a me ;-)

 uni!
Quindi lavorare in C++ va bene, usare altri linguaggi può 
significare spostare levette?

 sistema operativo in Assebly.
Qui non si sta parlando di linguaggi o caratteristiche tra 
linguaggi cosě diverse.

 Io sapevo che era stato Bjarne Stroustrup da solo, sbaglio?
Sě, sbagli.

 SI+interface nn fa.....
Ti e' gia' stato fatto notare che nessun lnguaggio di programmazione e'
piu' potente delle macchine di Turing, cioe' tutti i linguaggi
permettono di fare le stesse cose. Il problema fondamentale e' il come.
Cosi' come voi dite che l'ereditarieta' multipla non serve a molto si
potrebbe obbiettare che non servono a granche' nemmeno le classi o
l'assegnamento. Non mi pare pero' che stiate proponendo di tornare
tutti all'assembler. E perche' no? Dopo tutto ci si puo' fare tutto
quello che si puo' fare in Java o Delphi...
In realta' secondo me il punto e' che i programmatori si distinguono
per la tendenza a scrivere codice esteso oppure intenso. Chi tende a
scrivere codice esteso si preoccupa poco se si trova a riscrivere la
stessa funzionalita' piu' volte nello stesso programma o se il suo
codice contiene molte funzioni di servizio che non aggiungono valore
semantico ma sono rese necessarie dalle caratteristiche del linguaggio
(penso alle funzioni di delega di cui parlavo in un precedente
messaggio). Chi invece scrive codice intenso tende invece a cercare di
fattorizzare il codice il piu' possibile e a sfruttare tutti i
meccanismi di generalizzazione che il linguaggio di programmazione
mette a disposizione (nota che questa non e' la stessa cosa della
fissazione di alcuni programmatori C di infilare piu' istruzioni in
ogni singola linea di codice).
In generale quando si tratta di produrre codice nuovo nessuno dei due
approcci appare nettamente superiore all'altro; i vantaggi del secondo
si vedono in fase di manutenzione e sono legati al fatto che la minor
quantita' di codice e la sua maggiore generalita' portano a una
migliore copertura: il codice e' meglio esercitato ed eventuali
problemi emergono prima. Il codice intenso e' anche piu' facile da far
evolvere perche' in media ci sono meno punti di intervento per ciascuna
modifica. Per contro di solito il codice intenso e' piu' complesso di
quello esteso.
Costrutti come l'ereditarieta' multipla ed i template sono fondamentali
per poter scrivere codice intenso in C++. Alla fine della fiera a mio
avviso l'importante e' poter scegliere in ogni momento il costrutto
piu' adatto a rappresentare il concetto che si ha in mente. Per questo
mi da molto fastido l'esclusione aprioristica di particolari costrutti
da certi linguaggi di programmazione.
Ciao,
Nicola

Vedi la mia risposta ad un messaggio di G.R. un po' piu' sotto.
Ciao,
Nicola

 
Ciao

 tutti i costi" della superiorità di C++ rispetto agli altri linguaggi.
Pensa, a me il C++ non è nemmeno che fa impazzire.
-- 
non vincono, non vinceranno, non hanno domani
la forza è nel puntello impugnato da oneste fortissime mani

 tutti i costi" della superiorità di C++ rispetto agli altri linguaggi.
No, io ho spiegato in termini oggettivi perche' considero il C++ piu'
espressivo del Java in quanto dotato di ereditarieta' multipla e il mio
ragionamento non e' stato per ora confutato, se non in maniera del
tutto soggettiva.
Ciao,
Nicola

 delle rogne).
Qui si che entra in gioco la soggettivita', perche' l'adeguatezza a
risolvere un problema e' una proprieta' della coppia
linguaggio-programmatore, piu' che del linguaggio stesso. E in generale
se il C++ non serve, non serve neanche Java, che non ha abbastanza di
meno (o piuttosto di diverso) per rappresentare una categoria veramente
distinta.
 
Ciao,
Nicola

tutto soggettiva.
"Spiegare in termini oggettivi" e "considero" non sono espressioni che
vanno molto d'accordo, lo ammetterai ;-)
Comunque hai ragione, il tuo ragionamento non è stato ancora confutato
(a meno di scoprire che il modello SI+interface offra dei vantaggi
rispetto al C++), ma a me sarebbe anche solo bastato capire di che
ordine di grandezza il C++ si avvantaggia, con la MI, rispetto a Java
(o qualsiasi altro linguaggio dotato di SI).  Sono contento anche solo
scoprire che l'ordine di grandezza non è così grande ;-)
La complessità e la conseguente inadeguatezza del C++ per l'utilizzo
in moltissimi contesti (e con questo, bada, non voglio assolutamente
dire che non sia un ottimo linguaggio) è tale che usare un linguaggio
"più limitato", ma più facile da imparare, col quale riesci a fare
pressoché le stesse cose con pressoché la stessa facilità, non ti fa
sentire poi così "frustrato" ;-)

Se posso permettermi di esprimere la mia umile opinione, la MI virtuale
è una gran scocciatura, ma evita lo spreco di risorse non possibile
con la sola SI+contenimento (a meno di rivedere l'intero design).
Secondo punto, supponiamo di avere due classi ben funzionanti da tempo
con molti metodi pubblici e che, senza filosofeggiare sul 'is' o 'has',
debbano confluire in una nuova classe.
Mettendo da parte i casi di ambiguità sui nomi dei metodi, con la MI
non devo far altro che derivare da entrambe, con la SI devo ridefinire
tutti i metodi di una delle due classi.

 non devo far altro che derivare da entrambe[...]
Non esattamente, mi sembra.  Le classi devono essere progettate a 
priori per poter da esse derivare in questo modo.  Mi sembra 
questo il "limite" della MI in C++.  Dev'essere così o mi sbaglio?

Consideriamo i linguaggi al netto delle loro librerie, interfacce al
sistema, ecc.
Esiste una definizione formale di "potenza" di un linguaggio ed e' data
dall'insieme delle funzioni che permette di calcolare. In base alla
tesi di Church-Turing [1] questo insieme coincide per tutti i linguaggi
di programmazione.
Felleisen indica invece una misura formale del concetto di
"espressivita'" di un linguaggio che si basa sulla nozione che un
linguaggio e' piu' espressivo di un altro se contiene dei costrutti il
cui effetto non e' replicabile nell'altro senza uno sforzo non
trascurabile.
In base a questa definizione l'ereditarieta' multipla ed i template
contribuirebbero a rendere il C++ piu' espressivo di Java, mentre ad
esempio le inner classes renderebbero Java piu' espressivo del C++.
Mi pare che il solo fatto che il Java sia in buona parte il risultato
della sottrazione di costrutti al C++ sia un indizio forte della
maggiore espressivita' di quest'ultimo.
Sono pero' altrettanto convinto che la sola misura dell'espressivita'
non sia sufficiente per esprimere un giudizio assoluto sulla "bonta'"
di un linguaggio per due motivi fondamentali: intanto perche' secondo
me parlare di validita' di un linguaggio ha senso solo nel contesto dei
problemi che con tale linguaggio si vogliono risolvere ed una simile
valutazione non puo' prescindere dal contesto dove si opera: sistema,
librerie, ecc. In piu' la valutazione dell'efficacia di un linguaggio
non puo' prescindere da chi il linguaggio lo usa, sia per quanto
riguarda la competenza che per quanto riguarda la forma mentis.
Siamo d'accordo?
Ciao,
Nicola
[1] 
http://it.wikipedia.org/wiki/Tesi_di_Church-Turing
[2] 
http://citeseer.lcs.mit.edu/felleisen90expressive.html

Siamo d'accordo?
Certo, anche perché è facilmente condivisibile.  Ma non ho mai voluto
discutere di questo.  Mai mi verrebbe in mente di dire che, in
assoluto, C++ è "più buono" di Java o viceversa.
Credo sia opinione comune che la "bontà" di un linguaggio si riduca
alla migliore adattabilità del linguaggio alle proprie esigenze (di
qualsiasi natura esse siano).

 priori per poter da esse derivare in questo modo.
Dipende cosa intendi. Tipicamente se non sei in presenza di rischio
diamante puoi aggiungere un po' quando vuoi la possibilità di ereditare.
Quello che si dice è che ci sono classi che non sono pensate per essere
sottoclassate, ma questo è vero anche in altri linguaggi, con o senza
MI. Per esempio in ObjectiveC ci sono un buon numero di classi che sono
semplicemente troppo complesse da subclassare e per le quali viene
semplicemente suggerita la delega.


diamante puoi aggiungere un po' quando vuoi la possibilità di ereditare.
Il problema, però, è capire quando non sei in presenza di rischio
diamante...

con la MI, se le classi base sono virtuali, sei sempre a rischio
diamante.
Però sarebbe assurdo derivare sempre in modo virtuale per evitare il
rischio.
Secondo me, la MI virtuale può essere usata all'interno di un intero
progetto che ha una sua logica e non semplicemente per mettere insieme
due blocchettini del lego.
Nel caso si sia in presenza del diamante e non si possa derivare in
modo virtuale, la MI diventa semplicemente una SI+contenimento,
esplicita o implicita(derivando in MI, usando poi la classe comune da
una parte sola).
Ma in assenza del diamante i vantaggi sono molti di più degli
svantaggi.
Ciao

due blocchettini del lego.
Che vuoi dire?

 diamante...
Se erediti da un camper e da una casa, ci sono ottime probabilità che tu
non sia a rischio di diamante. Insomma, complessivamente la maggior
parte delle volte le classi *non* sono virtuali e non c'è bisogno che lo
siano.


 qualsiasi natura esse siano).
Direi che di volta in volta c'e' un nuovo compromesso tra le
caratteristiche del problema che dobbiaom affrontare ed il nostro
gusto, che a sua volta dipende dalla nostra competenza e dalla nostra
esperienza.
Ciao,
Nicola