La parola chiave const può essere usata per indicare al compilatore che una certa variabile non deve
essere più modificata una volta che è stata inizializzata, oppure per dichiarare metodi di una classe che
non possono alterare alcuna variabile membro della stessa classe.
Una pratica corretta nell'uso di const è quella di usarlo per proteggere dati che non devono essere
sovrascritti. Questo deve essere fatto nella fase di progettazione poiché forzare un const in un programma
dopo che ha già raggiunto uno stadio di maturazione dello sviluppo avrebbe considerevoli effetti a cascata.
La parola const può assumere molti significati, può essere usata in numerose posizioni della sintassi (anche
in alcune piuttosto innaturali)
Per capire cosa const sta proteggendo bisogna leggere da destra a sinistra.
const char * str; // puntatore a caratteri che non possono essere cambiati (anche se il puntatore può cambiare) char const * str; // come prima, solo un modo alternativo per scriverlo char * const str; // il puntatore non può essere modificato (anche se i caratteri nel buffer possono cambiare)
Analogamente per le reference (non ha senso applicare un const a una reference, dato che il senso stesso delle reference
è che non possono essere cambiate per riferire ad un altro oggetto):
const char & str; // reference verso un carattere che non può essere cambiato char const & str; // funziona come alla riga precedente
Finora sembra abbastanza semplice, ma costruzioni più complicate possono essere difficili da interpretare. Esempio:
char * const * data; // puntatore ad un puntatore non modificabile che punta ad un array di caratteri char const ** data; // puntatore ad un puntatore di caratteri non modificabili const char ** data; // puntatore ad un puntatore di caratteri non modificabili char ** const data; // puntatore non modificabile che punta ad un puntatore di caratteri char * const * const data; // puntatore non modificabile verso un puntatore non modificabile di caratteri
Esiste tra alcuni programmatori la convinzione che sia obbligatorio mettere const dopo il tipo dati, ma in realtà può essere scritto sia prima che dopo il tipo, se è un tipo dati regolare e non un puntatore (e.g. “char”). Usate la forma che si adatta meglio al vostro codice e agli standard di sviluppo del codice adottati dalla vostra organizzazione semplicemente cercando di essere consistenti. Se volete che const si applichi al puntatore la parola chiave deve apparire dopo l'asterisco.
E' inoltre una buona pratica dichiarare certi campi di un oggetto come const se questi esprimono una proprietà dell'oggetto che non cambierà durante la vita di esso.
L'uso più comune di const è di proteggere dati da cambiamenti che potrebbero essere fatti tramite puntatori o reference
void func ( const MyObject * data ); // MyObject non può essere cambiato in func void func ( const MyObject & data ); // MyObject non può essere cambiato in func
Notate che il posizionamento di const prima o dopo è irrilevante. Le righe seguenti sono equivalenti a quelle scritte sopra
void func ( MyObject const * data ); // same as ( const MyObject * ) void func ( MyObject const & data ); // same as ( const MyOjbect & )
Posizionare const dopo il puntatore o la reference cambia i suoi effetti. Un const che segue il puntatore cambia solo il puntatore, non
i dati che questo punta.
void func ( MyObject * const data ); // Protezione superflua di 'data' poiché passato per valore
All'interno di func siete liberi di manipolare MyObject, ma non il puntatore a MyObject. Tuttavia, siccome il puntatore è una variabile locale della funzione questo non è di grande utilità. Anche se cambiaste il valore del puntatore nulla verrebbe alterato al di fuori della funzione.
Posizionare const dopo una reference è completamente inutile e deve essere evitato. Le reference sono già implicitamente const
void func ( MyObject & const data ); // protezione inutile della reference a MyObject
In questo caso const protegge la reference, che non può venir manipolata.
A volte è utile ritornare dati privati di un oggetto preservando la loro non modificabilità al di fuori di esso.
const MyObject & MyClass::func ( MyObject & data ); // l'oggetto MyObject ritornato da func non può essere cambiato
Spesso metodi di un istanza non manipolano dati interni all'oggetto.
Questi metodi (spesso chiamati accessors) dovrebbero essere dichiarati const
L'effetto di questo è che il puntatore this all'interno del metodo, invece di essere definito come “MyClass *” sarà un “const MyClass *” così che l'oggetto non può essere modificato tramite questo puntatore.
void MyClass::func ( MyOjbect & data ) const; // Questa funzione non manipola dati della classe
Da notare che dichiarare un oggetto come const funziona come filtro sulla sua interfaccia dato che solo i metodi dichiarati const possono essere chiamati. Il motivo è legato a quanto già detto: chiamare un metodo di un oggetto 'const' significa passare implicitamente un puntatore all'oggetto di tipo “const MyClass *” e quindi un puntatore di un oggetto non modificabile.
void MyClass::const_func() const; void MyClass::func(); const MyClass object; object.const_func(); // ok object.func(); // non si può chiamare un metodo non-const di un oggetto const
Per la stessa ragione all'interno di un metodo const si può chiamare solo altri metodi const dell'oggetto.
Argomenti correlati: const_cast, mutable