Pagina didattica di G. Servizi
Home >> Linguaggio C++ >> vocabolario C++


livello medio

const

Serve a indicare al compilatore che il valore di una certa variabile non può essere modificato durante l'esecuzione del programma.

Va da sé, quindi, che una variabile così qualificata deve ricevere il proprio valore all'atto stesso della dichiarazione, attraverso un'immediata inizializzazione, pena il rilevamento di un errore da parte del compilatore, che segnala altresì errore a ogni eventuale operazione che comportasse la modifica del valore di una variabile qualificata const.

L'immediata inizializzazione è richiesta anche per la dichiarazione come const di un membro di una classe, quantunque, in tal caso, il compilatore GNU, prima dell'avvento dello standard 2011, lamentasse, a livello solo di avvertimento, la mancanza dell'ulteriore qualifica static.

In C++, oltre al significato fin qui espresso, la parola const può qualificare anche un metodo, ossia una funzione membro.

Esempi:

tanto in C che in C++, per variabili:

const int ciccio = 20;
const int cuccio(21);
const int coccio{22};
// si notino le tre diverse possibili immediate inizializzazioni


solo in C++, per una funzione membro:

class Ciccio {
//
//
void funzione( ) const;
//
//
};

void Ciccio :: funzione( ) const
{ }


Nel primo esempio le variabili di tipo intero ciccio, cuccio e coccio vengono qualificate const e, come tali, vengono doverosamente inizializzate; nel caso presente con le costanti intere 20, 21 e 22 rispettivamente. La seconda inizializzazione viene nomata "diretta" e le altre due, in base ai loro segni grafici, brace-or-equal. Se nel loro ambito di visibilità comparisse un'istruzione tale che, se fosse eseguita, ne modificherebbe il valore, come ad esempio ciccio++, questa sarebbe segnalata come errore.

Nel secondo esempio la funzione Ciccio :: funzione( ), membro della classe Ciccio, viene qualificata come const: questo significa che non può modificare in alcun modo l'oggetto della classe Ciccio che la esegue, sia esso stato qualificato o no a sua volta come const. D'altra parte, coerentemente, un oggetto che fosse stato qualificato come const potrebbe eseguire solo funzioni membro a loro volta così qualificate, pena, ancora una volta, la segnalazione di errore in compilazione.

Per concludere si osservi come la qualifica di const attribuita a un metodo di una classe sia sufficiente a consentirne l'overload con un altro metodo, anche con uguale lista di argomenti; altrettanto è consentito l'overload di funzioni che differiscano anche solo per la qualifica const attribuita a un argomento, se ricevuto per riferimento. Il seguente codice, che neppure esaurisce tutte le combinazioni di segnatura possibili, è, pertanto, se si prescinde dagli evidenti problemi psichici dell'autore, del tutto legittimo

# include <iostream>
using namespace std;

struct Ciccio
{void metodo(  ) const {cout << "cucu" << endl;}
void metodo(  ) volatile {cout << "KUKU" << endl;}
void metodo(  ) volatile const {cout << "kuku" << endl;}
void metodo(  ) {cout << "CUCU" << endl;}
void metodo(const Ciccio &u) {cout << "COCO" << endl;}
void metodo(volatile Ciccio &u) {cout << "KOKO" << endl;}
void metodo(const volatile Ciccio &u) {cout << "koko" << endl;}
void metodo(Ciccio &u) {cout << "coco" << endl;}
void metodo(const Ciccio &u) const {cout << "YOYO" << endl;}
void metodo(volatile Ciccio &u) const {cout << "WOWO" << endl;}
void metodo(const volatile Ciccio &u) volatile {cout << "yoyo" << endl;}
void metodo(Ciccio &u) const volatile {cout << "wowo" << endl;}} ciccio;

int main(  )
{const Ciccio ciccio;
volatile Ciccio coccio;
const volatile Ciccio caccio;
Ciccio cuccio;
:: ciccio . metodo(  ),
ciccio . metodo(  ),
coccio . metodo(  ),
caccio . metodo(  ),
:: ciccio . metodo(cuccio),
:: ciccio . metodo(coccio),
:: ciccio . metodo(ciccio),
:: ciccio . metodo(caccio),
ciccio . metodo(ciccio),
ciccio . metodo(coccio),
coccio . metodo(caccio),
caccio . metodo(::ciccio);}

Si provi a prevedere l'output e, se non si riesce, si esegua il programma e lo si interpreti.

Come ovvio corollario a quanto fin qui detto si sottolinea che non è lecito a una funzione che restituisca un riferimento rimovere l'eventuale qualifica const dal valore restituito: in altri termini la seguente fattispecie, e ogni altra equivalente, determina il rilevamento di un errore in compilazione

int &funza(const int &k)
{return k;} // ERRORE


mentre, altrettanto ovviamente, è consentito il viceversa:

const int &funza(int &k)
{return k;} // OK


Estensioni c++11

Il nuovo standard rende assai più rigoroso l'uso di questo qualificatore, specialmente quando lo si applica a membri di classi in congiunzione con la parola static. Come si è detto sopra, il compilatore GNU pre-2011 si lagnava, pur completando la compilazione, quando si effettuava la necessaria inizializzazione di una variabile membro qualificata const se non fosse stata anche qualificata static. Nello standard 2011 questa lagnanza, che appare essere un difetto della diagnostica più che qualcosa di rilevante, è stata opportunamente rimossa, in connessione col fatto che è ammessa l'inizializzazione di qualsiasi variabile membro (cfr. qui, al punto 7.)

Tuttavia esistono alcune particolarità connesse appunto con le qualifiche const e static (ma anche volatile) e precisamente:

Il codice seguente illustra tutto quanto detto nei precedenti quattro punti:

# include <iostream>
using namespace std;

struct Ciccio
{static const int a;
/* a non inizializzata, va ridichiarata e inizializzata
per poter essere usata, ma se non si usa non si commette errore */
static const int b;
/* b non inizializzata, va ridichiarata e inizializzata
per poter essere usata, ma se non si usa non si commette errore */
static const int c = 4; // inizializzata, non occorre ridichiararla
const int d = 6; // inizializzazione OBBLIGATORIA
static int e; /* NON VA INIZIALIZZATA; va piuttosto ridichiarata
per poter essere usata */
const float f = 4.5f; // inizializzazione OBBLIGATORIA
volatile int h = 31; // inizializzazione FACOLTATIVA
double const static g; // inizializzazione VIETATA
constexpr double const static i = 7.77; /* inizializzazione DOVUTA, const PLEONASTICO*/
} c;

const int Ciccio :: a = 1; /* inizializzazione necessaria; variabile
usata e non inizializzata nell'ambito della classe */
int Ciccio :: e = 8; /* ridichiarazione necessaria: e viene usata;
l'inizializzazione qui è facoltativa */
double const Ciccio :: g = 8.8; /* ridichiarazione e inizializzazione
NECESSARIE: g viene usata */
constexpr double Ciccio :: i; /* ridichiarazione DOVUTA, secondo lo standard,                                                     NONOSTANTE il compilatore GNU non la esiga.*/
/* la variabile membro b, non essendo mai usata, può rimanere
nel limbo della propria inutile dichiarazione */

int main(  )
{Ciccio c;
cout << c . a << ' ' << c . c << ' ' << :: c . d << ' ' << :: c . e
<< ' ' << c . f << ' ' << :: c . g << ' ' << (c . h = 27) << ' ' << c.i << endl;}

Torna in cima alla pagina