So che i riferimenti sono uno zucchero sintattico, quindi il codice è più facile da leggere e scrivere.
Ma quali sono le differenze?
Riassunto dalle risposte e dai link qui sotto:
NULL
), mentre un riferimento si riferisce sempre a un oggetto.&obj + 5
).Per chiarire un malinteso:
Lo standard C++ è molto attento ad evitare di dettare come un compilatore può implementare i riferimenti, ma ogni compilatore C++ implementa i riferimenti come puntatori. Cioè, una dichiarazione come:
int &ri = i;
se non è ottimizzata del tutto, alloca la stessa quantità di memoria come un puntatore, e mette l'indirizzo di
i
in quella memoria.
Quindi, un puntatore e un riferimento usano entrambi la stessa quantità di memoria.
Come regola generale,
Lettura interessante:
Un puntatore può essere riassegnato:
int x = 5;
int y = 6;
int *p;
p = &x;
p = &y;
*p = 10;
assert(x == 5);
assert(y == 10);
Un riferimento non può e deve essere assegnato all'inizializzazione:
int x = 5;
int y = 6;
int &r = x;
Un puntatore ha il proprio indirizzo di memoria e la propria dimensione sullo stack (4 byte su x86), mentre un riferimento condivide lo stesso indirizzo di memoria (con la variabile originale) ma occupa anche dello spazio sullo stack. Poiché un riferimento ha lo stesso indirizzo della variabile originale stessa, è sicuro pensare ad un riferimento come un altro nome per la stessa variabile. Nota: ciò a cui punta un puntatore può essere sullo stack o sull'heap. Idem per un riferimento. La mia affermazione in questa dichiarazione non è che un puntatore deve puntare allo stack. Un puntatore è solo una variabile che contiene un indirizzo di memoria. Questa variabile è sullo stack. Poiché un riferimento ha il proprio spazio sullo stack, e poiché l'indirizzo è lo stesso della variabile a cui fa riferimento. Maggiori informazioni su stack vs heap. Questo implica che c'è un indirizzo reale di un riferimento che il compilatore non vi dirà.
int x = 0;
int &r = x;
int *p = &x;
int *p2 = &r;
assert(p == p2);
Potete avere puntatori a puntatori a puntatori che offrono livelli extra di indirezione. Mentre i riferimenti offrono solo un livello di indirezione.
int x = 0;
int y = 0;
int *p = &x;
int *q = &y;
int **pp = &p;
pp = &q;//*pp = q
**pp = 4;
assert(y == 4);
assert(x == 0);
Il puntatore può essere assegnato direttamente a nullptr
, mentre il riferimento non può. Se vi impegnate abbastanza, e sapete come, potete rendere l'indirizzo di un riferimento nullptr
. Allo stesso modo, se ti impegni abbastanza puoi avere un riferimento a un puntatore, e quel riferimento può contenere nullptr
.
int *p = nullptr;
int &r = nullptr; <--- errore di compilazione
int &r = *p; <--- probabilmente nessun errore di compilazione, specialmente se il nullptr è nascosto dietro una chiamata di funzione, eppure si riferisce a un int inesistente all'indirizzo 0
I puntatori possono iterare su un array, potete usare ++
per andare al prossimo elemento a cui punta un puntatore, e + 4
per andare al quinto elemento. Questo non importa quale sia la dimensione dell'oggetto a cui punta il puntatore.
Un puntatore deve essere dereferenziato con *
per accedere alla posizione di memoria a cui punta, mentre un riferimento può essere usato direttamente. Un puntatore a una classe/struttura usa ->
per accedere ai suoi membri, mentre un riferimento usa un .
.
Un puntatore è una variabile che contiene un indirizzo di memoria. Indipendentemente da come un riferimento è implementato, un riferimento ha lo stesso indirizzo di memoria dell'elemento a cui fa riferimento.
I riferimenti non possono essere inseriti in un array, mentre i puntatori possono esserlo (Menzionato dall'utente @litb)
I riferimenti Const possono essere legati ai temporanei. I puntatori non possono (non senza qualche indirezione):
const int &x = int(12); //legale C++
int *y = &int(12); //illegale dereferenziare un temporaneo.
Questo rende const&
più sicuro per l'uso nelle liste di argomenti e così via.
A parte lo zucchero sintattico, un riferimento è un puntatore const
(non puntatore a un const
). Dovete stabilire a cosa si riferisce quando dichiarate la variabile di riferimento, e non potete cambiarlo in seguito.
Aggiornamento: ora che ci penso ancora un po', c'è una differenza importante.
Il target di un puntatore const può essere sostituito prendendo il suo indirizzo e usando un cast const.
Il target di un riferimento non può essere sostituito in nessun modo, a parte UB.
Questo dovrebbe permettere al compilatore di fare più ottimizzazione su un riferimento.