In che modo i mixin o i tratti sono migliori della semplice eredità multipla?


54

Il C ++ ha una semplice eredità multipla, molti progetti linguistici lo vietano come pericoloso. Ma alcune lingue come Ruby e PHP usano strana sintassi per fare la stessa cosa e chiamarla mixin o tratti. Ho sentito molte volte che i mixin / tratti sono più difficili da abusare della semplice eredità multipla.

Cosa li rende specificamente meno pericolosi? C'è qualcosa che non è possibile con mixin / tratti ma possibile con l'ereditarietà multipla in stile C ++? È possibile imbattersi nel problema dei diamanti con loro?

Sembra che stessimo usando l'ereditarietà multipla, ma solo scusando che si tratta di mixin / tratti in modo da poterli usare.


7
Non vedo l'ora che qualcuno pubblichi una risposta ben scritta, ponderata e approfondita a questo.
Robert Harvey,

7
Ruby e PHP non hanno introdotto mixin e tratti. Le Mixin sono state introdotte in Flavours (1980), I tratti sono stati introdotti in Squeak Smalltalk (2001). Flavours è stato il primo linguaggio orientato agli oggetti con eredità multipla e ha usato i mixin. Il C ++ ha ottenuto l'eredità multipla solo nella versione 2.0, che è stata rilasciata nel 1989, 9 anni dopo Flavours. Quindi, la domanda dovrebbe essere: Flavours ha dei semplici mixin, ma alcuni linguaggi come il C ++ introducono una strana sintassi per fare la stessa cosa e chiamarla eredità multipla.
Jörg W Mittag,

Risposte:


31

Esistono numerosi problemi con ereditarietà multipla quando vengono utilizzati con classi complete, ma ruotano tutti attorno all'ambiguità .

L'ambiguità si presenta in diversi modi:

  1. Se hai due classi base con lo stesso campo xe il tipo derivato chiede x, che cosa ottiene?
    • Se le due xvariabili hanno tipi incongruenti, è possibile inferirle.
    • Se sono dello stesso tipo, puoi provare a unirli nella stessa variabile.
    • Puoi sempre esporli come nomi strani e completamente qualificati.
  2. Se hai due classi base con la stessa funzione fcon firme identiche e qualcuno chiama f, come viene chiamato?
    • E se le due classi base condividessero un altro antenato virtuale comune (il problema del diamante).
    • Cosa succede se la funzione ha firme diverse, ma compatibili?
  3. Quando costruisci una classe con due classi base, quale costruttore delle classi base viene chiamato per primo? Quando distruggi l'oggetto, che viene ucciso?
  4. Quando disponi l'oggetto in memoria, come lo fai in modo coerente?
  5. Come gestite tutti questi casi con 3 classi base? 10?

E questo ignora cose come l'invio dinamico, l'inferenza del tipo, la corrispondenza dei modelli e altre cose che conosco di meno, che diventano più difficili quando il linguaggio supporta l'ereditarietà multipla di classi complete.

Tratti o Mix-in (o interfacce, o ...) sono tutti costrutti che limitano specificamente le capacità di un tipo in modo che non vi siano ambiguità. Raramente possiedono qualcosa da soli. Questo permette alla composizione dei tipi di diventare più fluida perché non ci sono due variabili o due funzioni ... c'è una variabile e un riferimento; una funzione e una firma. Il compilatore sa cosa fare.

L'altro approccio comune adottato è quello di forzare l'utente a "costruire" (o mescolare) il proprio tipo uno alla volta. Invece che le classi di base siano partner uguali nel nuovo tipo, aggiungi un tipo a un altro - sovrascrivendo qualsiasi cosa fosse presente (di solito con sintassi opzionale per rinominare e / o ri-esporre i bit sovrascritti).

C'è qualcosa che non è possibile con mixin / tratti ma possibile con l'ereditarietà multipla in stile C ++?

A seconda della lingua, in genere diventa problematico o impossibile unire le implementazioni di funzioni e archiviazione per variabili da più classi di base ed esporle nel tipo derivato.

È possibile imbattersi in un problema al diamante con loro?

A volte variazioni meno gravi appariranno in base alla tua lingua, ma di solito no. L'intero punto dei tratti è quello di rompere quel tipo di ambiguità.


Potete darmi un esempio del linguaggio / della tecnologia che consente tali tipi di "costruzione" per un'istanza?
Gherman,

@german - Non sono certo un esperto di Scala, ma la mia comprensione è che cosa fa la withparola chiave lì.
Telastyn,

"costrutti che limitano specificamente le capacità di un tipo in modo che non vi siano ambiguità": che tipo di limiti? Le interfacce non hanno variabili, quindi questo è un limite, ma i tratti di Scala e i moduli Ruby lo fanno. Sono limitati in altri modi?
David Moles,

@DavidMoles - questo è il modo comune, ho anche visto delle limitazioni sulle regole di invio virtuale (pensa alle interfacce esplicitamente implementate di C #).
Telastyn,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.