In questa sede, tratteremo uno dei concetti fondamentali della buona programmazione in C++ : il principio del minor privilegio. Il principio del minor privilegio non è niente altro che settare i giusti permessi ad una variabile.

 

Nell'esempio proposto di seguito non viene utilizzato il principio del minor privilegio.

 

#include <iostream>

using std::cout;

using std::endl;

void stampa( int * );

int main()

{

int number = 4;

stampa( &number );

return 0;

}

void stampa( int *numero )

{

cout << *numero << endl;

}

Come possiamo vedere, oltre alla classica funzione main, abbiamo la funzione stampa che accetta come parametro un indirizzo di memoria di un intero. La funzione non restituisce niente ( tipo di ritorno void )

Adesso esploriamo la funzione stampa assieme al suo parametro. Notiamo subito che il parametro di stampa è un puntatore ad intero non costante. La principale istruzione presente nella funzione è la medesima:

 

cout << *numero << endl;

 

In altre parole stampa il valore a cui punta la variabile numero. Come abbiamo visto, la funzione ha il solo compito di stampare il valore a cui punta la variabile puntatore; in teoria numero può puntare ad un altro indirizzo e può anche cambiare il valore nell'indirizzo puntato; tutto questo a noi non serve! Serve solamente stampare il valore presente nell'indirizzo a cui punta. Settiamo il principio del minor privilegio.

 

Ottenendo questo risultato:

 

#include <iostream>

using std::cout;

using std::endl;

 

void stampa( const int * const );

 

int main()

{

int number = 4;

stampa( &number );

return 0;

}

void stampa( const int * const numero )

{

cout << *numero << endl;

}

 

Come è possibile vedere, il codice è rimasto invariato, se non per il nuovo parametro della funzione stampa.

 

const int * const numero

 

Tranquilli, tutti quei const non sono messi a caso. C'è un ordine ben preciso che tra poco andremo a scoprire. Inizio svelando un trucchetto. La dichiarazione va letta da destra a sinistra tralasciando lo *. Come tutti noi sapremo, numero è un puntatore; quindi il valore di numero è un indirizzo di una variabile ( in questo caso contiene l'indirizzo della variabile number, passata come argomento ). Concludendo, leggendo da destra verso sinistra, possiamo interpretare la dichiarazione nel seguente modo:

 

il valore di numero ( cioè il suo indirizzo di memoria ) è costante. Questo significa che, la variabile puntatore in questione, non può cambiare il suo indirizzo.

 

Inoltre, il valore presente nell'indirizzo di memoria a cui punta la variabile numero è anche esso costante. Quindi, la variabile puntatore in questione non può cambiare il valore presente nell'indirizzo a cui punta.

 

Un altro uso di const:

 

const int * numero;

 

leggendo da destra verso sinistra, numero ( quindi l'indirizzo a cui punta numero ) non è costante. Il valore a cui punta la variabile puntatore è costante.

 

Ecco un esempio:

 

#include <iostream>

using std::cout;

using std::endl;

 

int main()

{

int var = 4;

int var1 = 1;

const int *varPtr; // varPtr non è costante. Il valore a cui punta varPtr invece lo è

varPtr = &var; // varPtr punta all'indirizzo di var

cout << "*varPtr = " << *varPtr << endl;

*varPtr = 3; // errore, non posso modificare il valore presente nell'indirizzo a cui punta varPtr, esso è costante

varPtr = &var1; // del tutto lecito. Il valore di varPtr non è costante

cout << "*varPtr = " << *varPtr << endl;

*varPtr = 4; // errore, non posso modificare il valore presente nell'indirizzo a cui punta varPtr, esso è costante

return 0;

}

 

Come è possibile vedere il valore di varPtr è variabile; il valore a cui punta varPtr è costante e quindi non modificabile. Un esempio di applicazione ad una funzione, potrebbe essere il seguente:

 

#include <iostream>

using std::cout;

 

void stampa ( const char * );

 

int main()

{

char string[] = "Questa e' una stringa\n";

stampa( string );

return 0;

}

 

void stampa( const char * stringa )

{

for( ; *stringa != '\0'; stringa++ )

cout << *stringa;

}

 

come è possibile notare stringa ad ogni fine istruzione, presente nel ciclo, punta all'indirizzo successivo fino a quando il valore presente nell'indirizzo è uguale a '\0', uscendo!

 

Analizziamo un ultimo caso:

 

char * const ptr;

 

Oramai il meccanismo dovrebbe essere chiaro. Lo ripetiamo per l'ultima volta: ptr ( l'indirizzo ) costante e il valore presente nell'indirizzo è variabile.

 

Un esempio immediato:

 

#include <iostream>

using std::cout;

 

#include <cctype>

using std::islower;

using std::isupper;

 

void converti ( char * const );

 

int main()

{

char string[] = "Questa e' una stringa\n";

converti( string );

cout << string;

return 0;

}

 

void converti( char * const stringa )

{

for ( unsigned short indice = 0; stringa[ indice ] != '\0'; indice++ )

{

if ( islower( stringa[ indice ] ) )

stringa[ indice ] = toupper( stringa[ indice ] );

else

stringa[ indice ] = tolower( stringa[ indice ] );

}

}

 

Da notare la variabile indice. Senza quella variabile, non avremmo potuto scorrere avanti array, perché l'indirizzo è costante.

 

Usate sempre il principio del minor privilegio, settando ad ogni variabile i giusti permessi, in

modo tale da proteggere SEMPRE i vostri dati. Tutto ciò ricadrà a vostro favore :)

 

 

P.S. C'è da dire che non è sempre utilizzabile e quindi applicabile la tecnica del minor privilegio. Const viene usato solamente per proteggere i dati. Se la nostra funzione avrà il bisogno di modificare i nostri dati? Semplice, non usiamo const! Un esempio pratico potrebbe essere il seguente:

 

#include <iostream>

using std::cout;

using std::endl;

 

void copia( char *, const char * );

 

int main()

{

const int max = 13;

char string1[ max ];

char string2[] = "ciao a tutti";

copia ( string1, string2 );

cout << string1 << endl;

return 0;

}

 

void copia( char * stringa1, const char * stringa2 )

{

for ( ; ( *stringa1 = *stringa2 ) != '\0'; stringa1++, stringa2++ );

}

Il primo parametro, come possiamo vedere, cambia il suo valore puntato e ad ogni ciclo punta all'indirizzo successivo. Il secondo parametro ( stringa2 ) non cambia il valore a cui punta ( infatti è costante ) bensì cambia l'indirizzo ad ogni fine ciclo ( infatti è variabile )