Come posso evitare il diamante della morte quando utilizzo l'ereditarietà multipla?

StackOverflow https://stackoverflow.com/questions/137282

  •  02-07-2019
  •  | 
  •  

Domanda

http://en.wikipedia.org/wiki/Diamond_problem

So cosa significa, ma quali passi posso fare per evitarlo?

È stato utile?

Soluzione

Un esempio pratico:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

Nota come la classe D eredita sia da B & amp; C. Ma sia B & amp; C eredita da A. Ciò comporterà l'inclusione di 2 copie della classe A nella tabella.

Per risolvere questo, abbiamo bisogno dell'eredità virtuale. È la classe A che deve essere praticamente ereditata. Quindi, questo risolverà il problema:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};

Altri suggerimenti

eredità virtuale. Ecco a cosa serve.

Continuerei a usare solo l'ereditarietà multipla delle interfacce. Sebbene a volte l'eredità multipla delle classi sia attraente, può anche essere confusa e dolorosa se ci si affida regolarmente.

L'ereditarietà è un'arma forte e forte. Usalo solo quando ne hai davvero bisogno. In passato, l'eredità del diamante era un segno che stavo andando molto lontano con la classificazione, dicendo che un utente è un "impiegato" ma sono anche un "listener di widget", ma anche un ...

In questi casi, è facile risolvere più problemi di ereditarietà.

Li ho risolti usando la composizione e i puntatori al proprietario:

Prima:

class Employee : public WidgetListener, public LectureAttendee
{
public:
     Employee(int x, int y)
         WidgetListener(x), LectureAttendee(y)
     {}
};

Dopo:

class Employee
{
public:
     Employee(int x, int y)
         : listener(this, x), attendee(this, y)
     {}

     WidgetListener listener;
     LectureAttendee attendee;
};

Sì, i diritti di accesso sono diversi, ma se riesci a cavartela con un tale approccio, senza duplicare il codice, è meglio perché è meno potente. (Puoi risparmiare energia per quando non hai alternative.)

class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

In questo gli attributi della Classe A si sono ripetuti due volte nella Classe D, il che rende più uso della memoria ... Quindi per risparmiare memoria creiamo un attributo virtuale per tutti gli attributi ereditati della classe A che sono memorizzati in una Vtable.

Bene, la cosa grandiosa del diamante temuto è che è un errore quando si verifica. Il modo migliore per evitare è capire in anticipo la struttura ereditaria. Ad esempio, un progetto su cui lavoro ha visualizzatori ed editor. Gli editor sono sottoclassi logiche di Viewer, ma poiché tutti i Viewer sono sottoclassi: TextViewer, ImageViewer, ecc., Editor non deriva da Viewer, consentendo quindi alle classi TextEditor e ImageEditor finali di evitare il diamante.

Nei casi in cui il diamante non è evitabile, usando l'eredità virtuale. Il più grande avvertimento, tuttavia, con basi virtuali, è che il costruttore per la base virtuale deve essere chiamato dalla classe più derivata, il che significa che una classe che deriva virtualmente non ha alcun controllo sui parametri del costruttore. Inoltre, la presenza di una base virtuale tende a incorrere in una penalità di prestazione / spazio al lancio attraverso la catena, anche se non credo che ci sia una penalità di molto di più oltre la prima.

Inoltre, puoi sempre usare il diamante se sei esplicito su quale base vuoi usare. A volte è l'unico modo.

Suggerirei un design di classe migliore. Sono sicuro che ci sono alcuni problemi che vengono risolti meglio attraverso l'ereditarietà multipla, ma controlla prima se c'è un altro modo.

In caso contrario, utilizzare funzioni / interfacce virtuali.

Usa l'ereditarietà per delega. Quindi entrambe le classi indicheranno una base A, ma devono implementare metodi che reindirizzino ad A. Ha l'effetto collaterale di trasformare i membri protetti di A in "privato". membri in B, C e D, ma ora non hai bisogno del virtuale e non hai un diamante.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top