2014-05-10 23 views
13

Assumere le classi D ed E e F tutti ereditano dalla classe base B, e che classe C eredita da D ed E.diamante eredità

(i) Quante copie di classe B compaiono in classe C?

(ii) In che modo l'utilizzo dell'ereditarietà virtuale modifica questo scenario? Spiega la tua risposta.

(iii) In che modo Java evita la necessità di ereditarietà multipla per molte delle situazioni in cui è possibile utilizzare l'ereditarietà multipla in C++?

Ecco alcune delle mie idee attuali, ma non sono affatto un esperto di C++!

(i) Se C eredita da D ed E che sono sottoclassi di B, allora D ed E sarebbero tecnicamente copie della loro super classe? Quindi se C eredita da D ed E significherebbe che ci sono 2 copie di B in C.

(ii) L'utilizzo di virtual è in qualche modo simile all'utilizzo di Abstract in Java (credo). Detto questo, significherebbe che non ci sarebbero più copie di B in C, in quanto l'istanza sarebbe ricondotta al livello necessario. Non sono sicuro di come pronunciare la mia spiegazione, ma diciamo che B ha una funzione chiamata print() che stampa "i am B" e C sovrascrive questa funzione inserisce le stampe "i am C". Se si chiama print() su C senza virtual si finisce per stampare "i am B", usando virtual significherebbe che stamperebbe "i am C".

(iii) La mia idea è che Java può utilizzare le interfacce per evitare l'uso dell'ereditarietà multipla. È possibile implementare più interfacce ma è possibile estendere solo una classe. Non sono sicuro di cos'altro aggiungere qui, quindi qualsiasi input o risorse rilevanti sarebbe utile.

+2

+1 un grande esempio di "Ho bisogno di aiuto ma ecco quello che ho provato" domanda – yizzlez

+1

La risposta breve per '(ii)' è che ci sarebbe solo una copia di B. Speriamo che una risposta spiegherà questo ulteriore – yizzlez

+2

+1, questo potrebbe anche aiutarti: http://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem –

risposta

3

(i) e (iii) hanno ragione. Nella mia esperienza comunque, la maggior parte del tempo in C++ quando ho usato l'ereditarietà multipla è stato perché le basi erano interfacce (un concetto che non ha supporto per le parole chiave in C++, ma è un concetto che è possibile eseguire comunque).

La prima frase di (ii) è corretta, tuttavia la seconda frase parla di funzioni virtuali, che è completamente diversa dall'ereditarietà virtuale. L'ereditarietà virtuale significa che esiste una sola copia di B e che lo D e lo E hanno entrambi la stessa copia della loro base. Non c'è differenza in termini di funzioni, ma la differenza si presenta in termini di variabili membro (e classi base) di B.

Se esiste una funzione che stampa la variabile membro Bfoo; quindi nel caso (ii) questa funzione stampa sempre lo stesso valore perché c'è solo uno foo, ma nel caso (i) chiamando quella funzione dalla classe base D si può stampare un valore diverso per chiamarlo dalla classe base E.

Il termine "diamante eredità" avvolge tutto questo in due parole che servono come un buon mnemonico :)

+0

Grazie per la tua risposta Matt. Approfondirò ulteriormente questo argomento, poiché ho detto che il materiale del corso è stato consegnato in modo insufficiente, quindi è un bel salto nel profondo. –

1

Sembra che tu abbia per lo più arrivati ​​alle risposte giuste, anche se il ragionamento ha bisogno di lavoro. Il problema chiave in gioco qui è la questione di "come disporre la memoria di un'istanza di C se eredita la stessa classe base due volte?"

i) Ci sono 2 copie della classe base B nel layout di memoria per un oggetto di tipo C.L'esempio fornito è un caso di "eredità del diamante", perché quando si disegna l'albero delle dipendenze/eredità, si disegna essenzialmente un diamante. Il "problema" con l'ereditarietà del diamante è essenzialmente quello di chiedere come posizionare l'oggetto in memoria. Il C++ è andato con due approcci, uno veloce, questo, duplicando i membri dei dati, e uno più lento, "l'ereditarietà virtuale". La ragione per adottare l'approccio non virtuale è che se si eredita una classe che non ha membri di dati (quale sarebbe un'interfaccia in Java), allora non c'è alcun problema con la "duplicazione dei membri di dati", perché non esistono (vedi la mia nota in fondo). È inoltre consigliabile utilizzare l'ereditarietà non virtuale se il proprio piano utilizza solo l'ereditarietà singola.

ii) Se si dispone di un virtual class C, allora questo è il modo di dire nel linguaggio C++ che si desidera che il compilatore esegua atti di eroismo per garantire che solo una copia di tutte/tutte le classi di base esista nel layout di memoria della tua classe derivata; Credo che anche questo subisca un leggero calo di prestazioni. Se ora usi qualsiasi membro 'B' da un'istanza 'C', farà sempre riferimento allo stesso posto in memoria. Tieni presente che l'ereditarietà virtuale non influisce sul fatto che le tue funzioni siano virtuali.

A parte: anche questo è completamente estraneo al concetto di una classe che è astratta. Per creare un abstract di classe in C++, imposta qualsiasi dichiarazione di metodo = 0, come in void foo() = 0;; farlo per qualsiasi metodo (incluso il distruttore) è sufficiente per rendere l'intera classe astratta.

iii) Java a titolo definitivo lo vieta. In Java esiste un'unica ereditarietà oltre alla possibilità di implementare qualsiasi numero di interfacce. Mentre le interfacce ti garantiscono la relazione "is-a" e la capacità di avere funzioni virtuali, evitano implicitamente i problemi che C++ ha con i layout dei dati e l'ereditarietà dei diamanti, poiché un'interfaccia non può aggiungere alcun membro di dati, ipso facto: non c'è confusione su come risolvere la posizione di qualsiasi membro di dati.

Un'estensione importante per iii è comprendere che la spedizione di chiamate di funzioni virtuali non ha alcun impatto se si "implementa la stessa interfaccia due volte". La ragione è che il metodo farà sempre la stessa cosa, anche se ce ne fossero più copie nella tua tabella virtuale; agisce solo sui dati della classe, non contiene dati che devono essere disambiguati.