Domanda

In C ++, devo specificare esplicitamente la parola chiave 'virtuale' per rendere una funzione membro 'sostituibile', poiché comporta un sovraccarico di creazione di tabelle e vpointer virtuali, quando una funzione membro viene resa sovrascrivibile (quindi ogni funzione membro è implicitamente non sostituibile per motivi di prestazioni).

Permette anche di nascondere una funzione membro (se non sovrascritta) quando una sottoclasse fornisce un'implementazione separata con lo stesso nome e firma.

La stessa tecnica è usata anche in C #. Mi chiedo perché Java abbia evitato questo comportamento e reso ogni metodo sovrascrivibile di default e fornito la possibilità di disabilitare il comportamento prevalente sull'uso esplicito della parola chiave "finale".

È stato utile?

Soluzione

La domanda migliore potrebbe essere " Perché C # ha metodi non virtuali? " O almeno, perché non sono virtuali per impostazione predefinita con l'opzione di contrassegnarli come non virtuali?

In C ++, c'è l'idea (come ha sottolineato Brian così bene) che se non lo vuoi, non lo paghi. Il problema è che se lo vuoi, questo di solito significa che finisci per pagare attraverso il naso. Nella maggior parte delle implementazioni Java, sono progettate esplicitamente per molte chiamate virtuali; le implementazioni della vtable tendono ad essere veloci, poco più costose delle chiamate non virtuali, il che significa che il vantaggio principale delle funzioni non virtuali è perso. Inoltre, i compilatori JIT possono incorporare funzioni virtuali in fase di esecuzione. Pertanto, per motivi di efficienza, in realtà è molto poco utilizzabile per funzioni non virtuali.

Quindi, si riduce in gran parte al principio della minima sorpresa. Ci dice che tutti i metodi si comportano allo stesso modo, non metà dei quali virtuali e metà non virtuali. Dal momento che abbiamo bisogno di avere almeno alcuni metodi virtuali per raggiungere questa cosa del polimorfismo, ha senso che siano tutti virtuali. Inoltre, avere due metodi con la stessa firma è solo chiedere di spararti nel piede.

Il polimorfismo impone inoltre che l'oggetto stesso debba avere il controllo su ciò che fa. Il suo comportamento non dovrebbe essere determinato se il cliente pensa che sia un FooParent o un FooChild.

EDIT: Quindi mi vengono invitate le mie affermazioni. Il prossimo paragrafo è congettura da parte mia, non una dichiarazione di fatto.

Un interessante effetto collaterale di tutto ciò è che i programmatori Java tendono ad usare le interfacce molto pesantemente. Poiché le ottimizzazioni del metodo virtuale rendono sostanzialmente inesistente il costo delle interfacce, consentono di utilizzare un Elenco (ad esempio) anziché un ArrayList e di cambiarlo per un LinkedList in un secondo momento con una semplice modifica di una riga e nessuna penalità aggiuntiva.

MODIFICA: Pony anche un paio di fonti. Pur non essendo le fonti originali, provengono da Sun che spiega alcuni dei meccanismi su HotSpot.

Inlining

VTable

Altri suggerimenti

Tratto da qui (# 34)

  

Non esiste una parola chiave virtuale in Java   perché tutti i metodi non statici sempre   usa l'associazione dinamica. In Java, il   il programmatore non deve decidere   se usare l'associazione dinamica. Il   la ragione per cui esiste virtuale in C ++ è così tu   può lasciarlo fuori per un leggero aumento   in efficienza quando stai sintonizzando   rendimento (o, in altre parole, " If   non lo usi, non paghi   esso "), che spesso provoca confusione   e spiacevoli sorprese. Il finale   parola chiave fornisce un po 'di latitudine per   ottimizzazione dell'efficienza - dice al   compilatore che questo metodo non può essere   ignorato, e quindi che potrebbe essere   legato staticamente (e reso in linea,   usando così l'equivalente di un C ++   chiamata non virtuale). Queste ottimizzazioni   sono al compilatore.

Un po 'circolare, forse.

Quindi la logica di Java è probabilmente qualcosa del genere: il punto centrale di un linguaggio orientato agli oggetti è che le cose possono essere estese. Quindi, in termini di puro design, ha davvero poco senso trattare l'estensibilità come il "caso speciale".

Ricorda che Java ha il lusso di compilare in fase di esecuzione. Quindi alcuni degli argomenti relativi alle prestazioni nella compilazione C ++ escono dalla finestra. In C ++, se una classe potrebbe essere sovrascritta, il compilatore deve compiere ulteriori passi. In Java, non c'è alcun mistero al riguardo: in qualsiasi momento, la JVM sa se un particolare metodo / classe è stato ignorato o meno, e questo è essenzialmente ciò che conta.

Nota che la parola chiave final riguarda essenzialmente la progettazione del programma, non l'ottimizzazione. La JVM non ha bisogno di queste informazioni per vedere se una classe / metodo è stata ignorata o meno !!

Se la domanda sta per porre qual è l'approccio migliore tra java e C ++ / C #, è stato già discusso in senso opposto in un altro thread e molte risorse disponibili in rete

Perché C # implementa i metodi come non virtuali per impostazione predefinita?

http://www.artima.com/intv/nonvirtual.html

La recente introduzione dell'annotazione @Override e la sua ampia adozione nel nuovo codice, suggeriscono che la risposta esatta alla domanda "Perché tutti i metodi java sono implicitamente sovrascrivibili?" è infatti perché il designer ha fatto un errore. (E l'hanno già risolto)

Oh! Otterrò un voto negativo per questo.

Java cerca di avvicinarsi a una definizione del linguaggio più dinamica, in cui tutto è un oggetto e tutto è un metodo virtuale. Vuole anche evitare ambiguità e costrutti difficili da capire, che i suoi progettisti hanno visto come un difetto in C ++, quindi nessun sovraccarico da parte dell'operatore, e in questo caso nessuna capacità di avere due firme di metodo pubblico su una gerarchia di classi che invocano metodi diversi a seconda del tipo della variabile che lo fa riferimento.

C # è più preoccupato per la stabilità delle sottoclassi e per assicurarsi che le sottoclassi si comportino in modo prevedibile. C ++ è preoccupato per le prestazioni.

Tre diverse priorità di progettazione, che portano a diverse scelte.

Direi che in Java il costo del metodo virtuale è basso rispetto ai costi dell'intera VM. In C ++ è un costo significativo, rispetto allo sfondo C simile ad un assieme. Nessuno deciderebbe di fare tutti i metodi chiamati tramite il puntatore di default come risultato della migrazione da C a C ++. È un cambiamento troppo grande.

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