Dal C al C++/Utilizzo basilare di librerie/Dichiarazioni e definizioni: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica |
|||
Riga 3:
== Le variabili riferimento ==
Nel linguaggio C, si possono definire variabili che non sono puntatori, come in "int a;", oppure variabili che sono puntatori a oggetti che non sono puntatori, come in "int * a;", oppure variabili che sono puntatori a oggetti che sono puntatori a oggetti, i
In C++ rimangono tutte queste possibilità, ma si aggiunge una nuova tipologia di variabile, il "riferimento".
Consideriamo le seguenti righe, di cui le prime due sono valide anche in C:
<source lang=cpp>
int a = 34;
int * b = & a;
int & c = a;
</source>
La prima riga definisce una variabile di nome "a", di tipo "int", inizializzata al valore intero "34".
La seconda riga definisce una variabile di nome "b", di tipo puntatore a "int", inizializzata all'indirizzo dell'oggetto rappresentato dalla variabile "a".
La terza riga, non valida in linguaggio C, definisce una variabile di nome "c", di tipo ''riferimento'' a "int", inizializzata all'oggetto rappresentato dalla variabile "a".
La variabile "a" rappresenta un oggetto, nel senso che dichiarando la variabile "a" si alloca un oggetto di tipo "int" contenente il valore 34, e anche nel senso che quando in seguito si usa la variabile "a" in un punto in cui è richiesta un'espressione, tale variabile costituisce un'espressione avente come valore il valore dell'oggetto associato alla variabile.
Anche la variabile "b" rappresenta un oggetto, che questa volta è un puntatore. Tale puntatore viene allocato quando si dichiara la variabile "b", e ha un valore che viene utilizzato nelle espressioni che usano la variabile "b".
Line 25 ⟶ 29:
Tale fatto si esprime dicendo che "c" è un ''alias'' di "a".
Per esempio, eseguendo il seguente codice.
<source lang=cpp>
c = 7;
cout << a;
</source>
Viene stampato il numero "7", in quanto l'oggetto riferito da "c" è lo stesso oggetto riferito da "a".
Line 33 ⟶ 41:
Si considerino le seguenti righe, supposte all'interno di una funzione:
<source lang=cpp>
int * b2;
int & c2; // Illegale
</source>
Le prime due continuano a definire variabili a cui corrispondono oggetti, anche se tali oggetti non sono inizializzati e quindi hanno valori indefiniti.
La terza riga è invece illegale sia in C che in C++.
Infatti, tenterebbe di dichiarare una variabile che costituisca un riferimento alternativo a un oggetto, ma non specifica a quale oggetto.
Siccome il collegamento di una variabile riferimento al suo oggetto può avvenire solo all'atto della definizione della variabile, tale variabile rimarrebbe per sempre non legata a un oggetto, e quindi completamente inutile.
Pertanto, il linguaggio considera illegale dichiarare variabili riferimento senza inizializzarle.
L'uso principale delle variabili riferimento, è però per dichiarare parametri di funzione.
Si consideri il seguente programma:
<source lang=cpp>
#include <iostream>
using namespace std;
double doppio2(double * a) { return *a * 2; }
double doppio3(double & a) { return a * 2; }
int main() {
double x = 1;
cout << doppio1(x);
cout << doppio2(&x);
cout << doppio3(x);
</source>
Le funzioni "
Tali funzioni moltiplicano per due il valore della variabile "x", e rendono il risultato della moltiplicazione.
In realtà, mentre la funzione "doppio1" prende una ''copia'' del valore di "x", le funzioni "doppio2" e "doppio3" prendono esattamente l'oggetto rappresentato dalla variabile "x".
Per comprendere tale differenza, si consideri il seguente programma:
<source lang=cpp>
#include <iostream>
using namespace std;
void raddoppia2(double * a) { *a *= 2; }
void raddoppia3(double & a) { a *= 2; }
int main() {
double x = 1;
raddoppia1(x);
Line 73 ⟶ 91:
raddoppia3(x);
cout << x;
</source>
Le funzioni "
Chiamando la funzione "raddoppia1", il valore dell'oggetto riferito dalla variabile "x" viene copiato sullo stack.
All'interno della funzione, tale valore viene riferito dal parametro "a".
La variabile "a" rappresenta un oggetto distinto da quello rappresentato dalla variabile "x", anche se inizialmente ha lo stesso valore.
Il corpo della funzione modifica tale oggetto allocato sullo stack, e, quando la funzione termina, l'oggetto modificato viene deallocato dallo stack, senza che ci siano effetti sul mondo esterno.
Quindi l'oggetto rappresentato dalla variabile "x" continua a valere "1".
Chiamando la funzione "raddoppia2", è l'''indirizzo'' dell'oggetto riferito dalla variabile "x" ad essere copiato sullo stack.
All'interno della funzione, tale valore viene riferito dal parametro "a".
Il corpo della funzione modifica l'oggetto puntato da tale valore allocato sullo stack, che è poi lo stesso oggetto associato a "x", e, quando la funzione termina, l'oggetto puntatore viene deallocato dallo stack.
Quindi l'oggetto rappresentato dalla variabile "x" è stato raddoppiato, passando dal valore "1" al valore "2".
Chiamando la funzione "raddoppia3", si ''comunica'' alla funzione che il parametro "a" dovrà rappresentare lo stesso oggetto rappresentato dalla variabile "x".
Questa ''comunicazione'' può avvenire copiando sullo stack l'indirizzo dell'oggetto rappresentato dalla variabile "x".
Il corpo della funzione modifica l'oggetto puntato da tale valore allocato sullo stack, e, quando la funzione termina, l'oggetto puntatore viene deallocato dallo stack.
Siccome la variabile "a" è solo un alias della variabile "x", le modifiche apportate ad "a" risultano applicate anche a "x".
Quindi l'oggetto rappresentato dalla variabile "x" è stato raddoppiato, passando dal valore "2" al valore "4".
Come si vede, il corpo della funzione "raddoppia3" è uguale a quello della funzione "raddoppia1", ma il comportamento è uguale a quello della funzione "raddoppia2".
Questi fatti si esprimono con la frase: '''I riferimenti hanno la sintassi dei valori, ma la stessa semantica dei puntatori'''. Questa frase significa: '''I riferimenti hanno lo stesso aspetto dei valori, ma lo stesso comportamento dei puntatori'''.
== Il modificatore "const" ==
Line 101 ⟶ 120:
In C++ esiste la parola-chiave "const" (abbreviazione della parola inglese "constant", cioè "costante"), che serve a modificare la dichiarazione di variabili e funzioni.
Ecco alcuni esempi di utilizzo:
<source lang=cpp>
const int x1 = 0;
const int * x2;
int * const x3 = 0;
const int * const x4 = 0;
</source>
La variabile "
La variabile "
Tale variabile può essere usata per modificare l'oggetto puntatore, rendendolo nullo o facendolo puntare a un altro oggetto, ma non può essere usata per modificare l'oggetto puntato, qualunque esso sia.
La variabile "x3" rappresenta un oggetto di tipo puntatore a "int".
Tale variabile non può essere usata per modificare l'oggetto puntatore, che quindi dovrà sempre puntare allo stesso oggetto, ma può essere usata per modificare l'oggetto puntato.
Infine, anche la variabile "x4" rappresenta un oggetto di tipo puntatore a "int".
Tale variabile non può essere usata per modificare l'oggetto puntatore, che quindi dovrà sempre puntare allo stesso oggetto, e non può essere usata neanche per modificare l'oggetto puntato.
L'inizializzazione di "x1", di "x3", e di "x4" è obbligatoria, in quanto si tratta di variabili non modificabili, e quindi non gli si potrà assegnare un valore in seguito.
La variabile "x2" invece può non essere inizializzata, in quanto, essendo modificabile, le si potrà assegnare un valore in seguito.
Quello che non sarà modificabile è l'oggetto puntato da "x2", qualunque esso sia.
Ecco quali sono gli utilizzi
<source lang=cpp>
x3 = 0; // Illegale
</source>
Solo la variabile "x2" è "non-const", e quindi solo lei è modificabile.
<source lang=cpp>
*x1 = 1; // Illegale
*x2 = 1; // Illegale
*x3 = 1; // OK
*x4 = 1; // Illegale
</source>
La variabile "x1" non è un puntatore, e quindi è ovviamente illegale dereferenziarla come se lo fosse.
Le variabili "x2", "x3" e "x4" sono dei puntatori, quindi si può tentare di assegnare un valore all'oggetto puntato.
Tuttavia "x2" e "
Si noti che il modificatore "const" non si applica agli oggetti, ma alle variabili, come mostrato dal seguente esempio:
<source lang=cpp>
int main() {
int x = 1;
int * p1 = &x;
Line 137 ⟶ 175:
*p1 = 3;
*p2 = 4; // Illegale
</source>
Sia la variabile "p1" che la variabile "p2" sono puntatori inizializzati a puntare allo stesso oggetto, associato anche alla variable "x".
Tale oggetto in sé è modificabile, infatti lo si può modificare sia assegnandogli il valore "2" tramite la variabile "x", sia assegnandogli il valore "3" tramite il puntatore "p1".
Tuttavia, il tentativo di assegnargli il valore "4" tramite il puntatore "p2" è un errore sintattico, in quanto la variabile "p2" ha il tipo di puntatore a oggetto non modificabile.
Il modificatore "const" si può applicare anche ai parametri di qualunque funzione, e alle funzioni membro stesse.
Per esempio:
<source lang=cpp>
void f1(const int& x5) {
x5 = 1; // Illegale
int x6;
void f2() const {
x6 = 2; // Illegale
}
</source>
Il parametro "x5" è di tipo riferimento a un "int" non modificabile, e quindi è illegale assegnare a tale variabile.
Un riferimento non può mai essere reindirizzato a un altro oggetto, pertanto la seguente riga è erronea:
<source lang=cpp>
void f1(int & const x5) { } // Illegale
</source>
Nella classe "C", la funzione membro "f2" non prende argomenti, ma presenta la parola "const" appena prima del corpo.
Line 164 ⟶ 210:
Gli scopi del modificatore "const" sono i seguenti:
* Rendere il codice autodocumentante, e quindi più comprensibile. Il programmatore potrebbe scrivere un commento come "questa routine si limita a leggere l'oggetto", ma
* Evidenziare errori di programmazione. Se una variabile non deve essere modificata e invece lo è, si tratta di un errore logico che verrà segnalato dal compilatore.
* Rendere il codice più efficiente. Se il compilatore sa che una variabile ha un valore costante, può propagare il suo valore ovunque è usata la variabile. Per variabili locali, questo viene già fatto dai compilatori ottimizzanti, ma normalmente non viene fatto per le variabili globali. Questo fatto rende inutile e sconsigliato l'uso delle macro del preprocessore (la direttiva "#define") per definire costanti.
== L'
Un concetto introdotto dal C++ è il sovraccaricamento (in inglese "overload"), che si applica alle funzioni e agli operatori.
=== L'
Il seguente programma è errato in C, ma valido in C++:
|