2012-03-12 1 views
12

Mi sono appena imbattuto nella dichiarazione nel titolo. La citazione completa è:"Come regola generale, rendi tutti i tuoi metodi virtuali" in C++: un buon consiglio?

Come regola generale, fanno tutti i metodi virtuali (tra cui il destructor, ma non costruttori) per evitare problemi connessi con omissione della parola chiave virtuale.

ho trovato questo nel libro Wrox professionale C++. You can google it to check.

C'è qualcosa? Avrei pensato di fornire solo punti di estensione selezionati, non estensibilità di default. Ad esempio, a 2001 article by Herb Sutter says so. Qualcosa è cambiato radicalmente da allora per fare il contrario della norma? (Nota che sono un noob in C++ quindi non ho seguito la discussione per l'ultimo decennio.)

+2

Ho sentito alcune volte ma nessuno è mai riuscito a convincermi a seguirlo. Sembra un po 'fragile per me. Tendo ad essere d'accordo con la tua analisi. Ovviamente sarà sempre molto soggettivo. –

+1

L'inclusione della prima funzione virtuale in una classe non complica la rappresentazione interna, forzando un vtable o qualcosa del genere? Avrei pensato che fosse "Vai solo virtuale quando devi davvero". –

+1

... a meno che non si stia progettando un gioco. – Marlon

risposta

10

C'è qualcosa?

il consiglio è BAD, non c'è dubbio su di esso. Leggere qualcosa del genere sarebbe sufficiente per stare lontano dal libro e dal suo autore.

Vedete, la parola chiave virtuale indica "è possibile o dovrebbe sovrascrivere questo metodo - è stato progettato per questo".

Per qualsiasi compito non banale, non riesco a immaginare un ragionevole sistema di classi che permetta all'utente (ad esempio un altro programmatore) di sovrascrivere ogni singolo metodo in ogni classe derivata. È normale avere una classe astratta di base con solo metodi virtuali. Tuttavia, una volta che si iniziano a creare classi derivate, non c'è motivo per dare uno schiaffo "virtuale" a tutto - alcuni metodi non hanno bisogno di essere estensibili.

Rendere tutto virtuale significa che in qualsiasi punto del codice, indipendentemente dal metodo utilizzato, non si può mai essere sicuri che la classe farà ciò che si desidera, perché qualcuno potrebbe aver annullato il metodo, interrompendolo nel processo (Secondo la legge di Murphy succederà). Ciò renderà il tuo codice inaffidabile e difficile da mantenere. Un'altra cosa molto interessante è il modo in cui i metodi virtuali vengono chiamati nei costruttori. Fondamentalmente, seguendo questo consiglio si sacrifica la leggibilità/affidabilità del codice in cambio di un errore di battitura non comune. In mia opinione, non ne vale la pena.

In confronto, il metodo non virtuale garantisce che indipendentemente da ciò che accade, a questo punto del codice, il codice funzionerà sempre come previsto (senza contare gli errori che non hai ancora scoperto). Cioè qualcun altro non sostituirà il tuo metodo con alternative rotte.

Il consiglio mi ricorda un errore comune che alcuni programmatori newbie tendono a fare: invece di sviluppare una soluzione semplice che risolverà il problema, si distraggono e tentano di rendere il codice universale ed estensibile. Di conseguenza, il progetto impiega più tempo per finire o non diventa mai completo - perché la soluzione universale per ogni scenario possibile richiede più tempo di impegno/sviluppo di una soluzione localizzata limitata solo al problema attuale.

Invece di seguire questo consiglio "virtuale", consiglierei di attenersi a Murphy's Law e KISS principle. Hanno funzionato bene per me. Tuttavia, non è garantito che funzionino bene per tutti gli altri.

+0

Grazie, @SigTerm. Consentitemi di dire che ho trovato il libro in questione - edizione 2011, aggiornato per C++ 11 - essere altrimenti un'introduzione molto utile alla lingua. Sto leggendo lentamente, e varie fonti in parallelo per rendere le cose veramente chiare per me, e io ero già pronto all'uso dell'estensibilità di default come in Java. Quindi questo consiglio mi ha sorpreso. - Stai dicendo: "Un'altra cosa molto interessante è il modo in cui i metodi virtuali vengono chiamati nei costruttori". [Cerca su Google] (http://www.google.com?q=virtual+method+in+costructor); sembra che dovresti evitare di chiamare metodi virtuali nei medici. – Lumi

0

Non può far male. Se non hai una sottoclasse che ridefinisce la funzione, nulla è diverso. Posso vedere questa tecnica essere utile se hai molta eredità in corso e potresti perdere la cognizione di ciò che le classi ereditano.

Personalmente, non realizzo alcun metodo di classe virtuale ... ma sono solo io. Avere tutto virtual sembra rendere le cose più confuse, imho.

+2

Può far male. Avere> 0 funzioni virtuali aumenta la dimensione dell'oggetto. Può potenzialmente oscurare l'autore l 'intento Può potenzialmente impedire al compilatore di inlining. –

+0

L'altra lingua che uso frequentemente (Python) rende ogni funzione virtuale di default (non c'è una parola chiave). Quindi, usare questo approccio non può essere poi così male. – austin1howard

+0

Capisco cosa intendi @OliCharlesworth ... Stavo solo dicendo che il codice funzionerebbe ancora correttamente. Ma d'accordo ... non è l'approccio più efficiente. – austin1howard

11

Non sono d'accordo con il principio.

In passato, alcuni erano preoccupati per l'uso eccessivo di virtual a causa di problemi di prestazioni. Questo è ancora un po 'valido, ma non eccessivamente problematico sull'hardware di oggi. (Tenete a mente, la maggior parte delle altre lingue incorre in sanzioni simili in questi giorni.Ad esempio, l'iPhone 2G da 400 MHz utilizzava l'Objective C che incorre in una chiamata di metodo virtuale su ogni chiamata di funzione.)

Penso che dovresti usare solo virtual sui metodi dove sembra utile e ragionevole voler sovrascriverlo in una sottoclasse. Per me, serve come suggerimento ad altri programmatori (o al tuo futuro sé) come "questo è un posto in cui le sottoclassi possono personalizzare sensibilmente il comportamento". Se la sostituzione del metodo in una sottoclasse sarebbe confusa o strana da implementare, non utilizzare virtual.

Inoltre, per semplici setter e getter, è probabilmente una cattiva idea in quanto inibisce l'inlining.

-1

IMHO, questa è una buona regola per i principianti con C++. Non è davvero dannoso se non in scenari molto specifici (quelli affrontati dai programmatori che sanno esattamente quali sono i compromessi della parola chiave virtuale), ed è una cosa in meno a cui pensare.

Tuttavia, come ogni regola empirica, semplifica eccessivamente la situazione, e quando si cresce a pensare a come comunicare ad altri programmatori quali metodi possono o non possono essere buoni candidati per la ridefinizione, si è superata la regola generale.

2

Ci sarà una piccola perdita di prestazioni e pochi byte di memoria sprecati.

Il vero problema è che rende il codice meno mantenibile perché stai dicendo qualcosa sulla funzione che non è vera. Potrebbe causare molta confusione.