Questo riguarda principalmente la seconda riga: migliori pratiche, assegnazioni, parametri di funzione ecc.
Pratica generale. Prova a fare tutto il const
possibile. O, per dirla in un altro modo, fai tutto const
per cominciare, quindi rimuovi esattamente il set minimo di const
s necessario per consentire al programma di funzionare. Questo sarà di grande aiuto nel raggiungimento della correttezza const e contribuirà a garantire che i bug sottili non vengano introdotti quando le persone cercano di assegnare in cose che non dovrebbero modificare.
Evita const_cast <> come la peste. Esistono uno o due casi d'uso legittimi, ma sono molto pochi e distanti tra loro. Se stai cercando di cambiare un const
oggetto, farai molto meglio per trovare chiunque lo abbia dichiarato const
al primo passo e discutere la questione con loro per raggiungere un consenso su cosa dovrebbe accadere.
Il che porta molto bene agli incarichi. Puoi assegnare qualcosa solo se non è const. Se vuoi assegnare qualcosa che è const, vedi sopra. Ricorda che nelle dichiarazioni int const *foo;
e int * const bar;
cose diverse lo sonoconst
- altre risposte qui hanno affrontato il problema in modo ammirevole, quindi non ci entrerò.
Parametri di funzione:
Passa per valore: ad es. void func(int param)
Non ti interessa in un modo o nell'altro sul sito chiamante. Si può argomentare che ci sono casi d'uso per dichiarare la funzione come void func(int const param)
ma che non ha alcun effetto sul chiamante, solo sulla funzione stessa, in quanto qualunque valore viene passato non può essere modificato dalla funzione durante la chiamata.
Passa per riferimento: ad es. void func(int ¶m)
Ora fa la differenza. Come appena dichiarato, func
è consentito cambiare param
e qualsiasi sito di chiamata dovrebbe essere pronto a far fronte alle conseguenze. La modifica della dichiarazione in void func(int const ¶m)
modifica del contratto e garanzie che func
ora non possono cambiare param
, il che significa che ciò che viene passato è ciò che verrà restituito. Come altri hanno notato, questo è molto utile per passare a buon mercato un oggetto di grandi dimensioni che non si desidera modificare. Passare un riferimento è molto più economico del passare un oggetto grande per valore.
Passare da puntatore: ad esempio, void func(int *param)
e void func(int const *param)
Questi due sono praticamente sinonimo con le loro controparti di riferimento, con l'avvertenza che la funzione chiamata ha ora bisogno di verificare la presenza di nullptr
meno che qualche altro assicura garanzia contrattuale func
che non potrà mai ricevere nullptr
in param
.
Opinione su questo argomento. Dimostrare la correttezza in un caso come questo è incredibilmente difficile, è fin troppo facile commettere un errore. Quindi non correre rischi e controlla sempre i parametri del puntatore per nullptr
. Ti risparmierai dolore e sofferenza e sarà difficile trovare insetti a lungo termine. E per quanto riguarda il costo del controllo, è sporco a buon mercato e nei casi in cui l'analisi statica integrata nel compilatore può gestirlo, l'ottimizzatore lo eliminerà comunque. Attiva Link Time Generation per MSVC o WOPR (credo) per GCC e otterrai un programma ampio, vale a dire anche nelle chiamate di funzione che attraversano un confine del modulo di codice sorgente.
Alla fine di tutto quanto sopra è un caso molto solido per preferire sempre i riferimenti ai puntatori. Sono solo più sicuri a tutto tondo.