Contesto:
In C++, lavorare con la memoria è fondamentale, il linguaggio offre al programmatore un controllo completo per l'efficienza. Inizialmente esistevano solo i concetti di "stack" (pila) e "heap" (mucchio) come aree di allocazione della memoria dinamica e automatica.
Problema:
La scelta dell'area di collocazione di una variabile determina il suo ciclo di vita, disponibilità, velocità di allocazione e deallocazione della memoria, oltre ai rischi (perdite, danneggiamenti dello stack, frammentazione).
Soluzione:
L'allocazione stack è utilizzata per variabili locali con un ciclo di vita noto. L'allocazione heap è per oggetti che necessitano di un ciclo di vita dinamico o di un grande volume di memoria. È consigliato minimizzare la gestione manuale dell'heap, preferendo lo stack e utilizzando puntatori smart per lavorare con la memoria dinamica.
Esempio di codice:
// Allocazione stack int a = 5; // Allocazione heap int* b = new int(10); // Lavorare con un puntatore smart #include <memory> auto ptr = std::make_unique<int>(15);
Cosa succede se restituisci un puntatore a una variabile locale da una funzione?
Si verifica un comportamento indefinito: dopo l'uscita dalla funzione, la memoria viene "liberata" (in realtà lo stack non viene pulito, ma i dati possono essere sovrascritti).
Esempio di codice cattivo:
int* foo() { int a = 42; return &a; // sbagliato! }
La memoria allocata nello stack può perdere?
No, la memoria dello stack viene sempre liberata automaticamente all'uscita dall'ambito — si verifica solo il cosiddetto overflow dello stack (stack overflow), ma non perdite.
È sufficiente usare sempre delete per liberare la memoria dinamica?
No, è molto più comune utilizzare puntatori smart (std::unique_ptr, std::shared_ptr) per evitare delete dimenticati e doppie deallocazioni.
Caso negativo:
Lo sviluppatore utilizzava new per tutti gli oggetti temporanei, dimenticava di liberarli — nel tempo, in grandi applicazioni si sono verificate perdite di memoria.
Pro: inizialmente veloce e comodo Contro: programmi instabili, crescita della memoria, crash
Caso positivo:
Utilizzo di variabili locali e puntatori smart per oggetti temporanei e ausiliari. Nessun delete esplicito, tutto viene liberato automaticamente.
Pro: assenza di perdite, affidabilità Contro: è necessario comprendere i moderni approcci alla gestione della memoria