Storia della questione:
I puntatori sono uno degli strumenti fondamentali in C++ sin dalla nascita del linguaggio, alla base della gestione della memoria e delle strutture dinamiche, nonché dell'interazione con le librerie di sistema a basso livello. I riferimenti sono stati aggiunti successivamente (con l'apparizione di C++) per semplificare la sintassi e aumentare la sicurezza del codice.
Problema:
Gli errori nella gestione della memoria sono uno dei problemi più comuni in C++, specialmente con l'uso improprio dei puntatori (puntatori pendenti, perdite di memoria). A volte i neofiti non capiscono quando usare un riferimento e quando un puntatore, oppure presumono erroneamente che siano completamente intercambiabili.
Soluzione:
Un puntatore (T*) è una variabile che memorizza l'indirizzo di un oggetto, può essere nullptr, supporta l'aritmetica, consente l'allocazione e la deallocazione dinamica, e può cambiare a cosa punta.
Un riferimento (T&) è un alias di un altro oggetto esistente. I riferimenti devono essere inizializzati al momento della creazione, non possono essere "vuoti" (riferendo a null), non supportano l'aritmetica, ma sono più comodi e sicuri (ad esempio, quando vengono passati a funzioni).
Esempio di codice:
void increment_pointer(int* p) { if(p) ++(*p); } void increment_reference(int& r) { ++r; } int main() { int a = 5; increment_pointer(&a); increment_reference(a); }
Caratteristiche principali:
È possibile modificare un riferimento dopo la sua inizializzazione affinché punti a un altro oggetto?
No. Un riferimento in C++ è un alias costante. Dopo l'inizializzazione, un riferimento punterà sempre allo stesso oggetto, a differenza di un puntatore, che può essere reindirizzato.
Esempio di codice:
int a = 1; int b = 2; int& ref = a; // ref = b - assegna a ref il valore di b, ma ref continua a puntare a a
È possibile creare un "riferimento nullo" o un riferimento a nullptr?
No, lo standard non consente riferimenti a oggetti inesistenti. Questo porta a comportamenti indefiniti.
Esempio:
int* p = nullptr; // int& r = *p; - UB
Qual è la differenza tra int const ptr e const int ptr?**
int* const ptr è un puntatore costante a un int (il puntatore non può essere reindirizzato, ma il valore può essere cambiato). const int* ptr è un puntatore a un int costante (non è possibile modificare il contenuto a cui punta, ma il puntatore può essere reindirizzato).
Esempio di codice:
int a = 1, b = 2; const int* ptr1 = &a; // *ptr1 non può essere modificato, ptr1 = &b - possibile int* const ptr2 = &a; // *ptr2 può essere modificato, ptr2 = &b - non possibile
Un giovane sviluppatore ha utilizzato puntatori per passare parametri, senza controllarli per verificarne il valore nullptr, causando un arresto anomalo in caso di errori.
Vantaggi:
Svantaggi:
Un sviluppatore esperto ha utilizzato int& quando la trasmissione è garantita e int* quando è possibile un nullptr, controllando sempre il puntatore per il valore nullo.
Vantaggi:
Svantaggi: