La memoria dinamica permette di creare (e distruggere) variabili e array a runtime, quando non conosciamo a priori quanti dati serviranno.
In C++ la memoria dinamica si gestisce con gli operatori new e delete (e le loro varianti per gli array new[] e delete[]).
1️⃣ Stack vs Heap (perché serve new)
- Stack: memoria automatica. Le variabili locali esistono solo finché la funzione non termina. Veloce, ma dimensione e durata sono fisse.
- Heap: memoria dinamica. Crei oggetti quando ti servono e li distruggi quando hai finito. Flessibile, ma devi gestire tu la liberazione.
Esempio rapido: - Stack: int a = 5; // si distrugge da sola a fine blocco - Heap: int* p = new int; // vive finché non fai delete p;
2️⃣ new e delete (variabile singola)
new Tipo alloca sullo heap un oggetto di tipo Tipo e restituisce un puntatore ad esso.
delete libera la memoria precedentemente allocata con new.
#include <iostream>
using namespace std;
int main() {
int* p = new int; // alloca un int non inizializzato
*p = 42; // lo uso tramite dereferenziazione
cout << *p << endl; // 42
delete p; // libera la memoria
p = nullptr; // azzera il puntatore (buona pratica)
}
Inizializzazione diretta al new:
int* q = new int(7); // inizializza a 7
double* d = new double{3.14}; // lista di inizializzazione
delete q;
delete d;
3️⃣ new[] e delete[] (array dinamici)
Per un numero di elementi deciso a runtime si usa new[] e si libera con delete[]:
int n;
cin >> n;
int* v = new int[n]; // array di n interi su heap
for (int i = 0; i < n; i++) v[i] = i*i;
for (int i = 0; i < n; i++) cout << v[i] << " ";
delete[] v; // IMPORTANTE: delete[] per gli array
v = nullptr;
4️⃣ Struct e memoria dinamica
Puoi dinamicamente creare anche struct (e array di struct). Si usa l operatore freccia -> per accedere ai campi.
struct Studente {
string nome;
int eta;
float media;
};
int main() {
// Oggetto singolo dinamico
Studente* ps = new Studente{"Luca", 18, 7.9f};
cout << ps->nome << " - " << ps->media << endl;
ps->media = 8.4f;
delete ps; ps = nullptr;
// Array dinamico di struct
int n = 3;
Studente* classe = new Studente[n]{
{"Anna", 17, 8.1f},
{"Marco", 18, 9.0f},
{"Giulia", 17, 7.2f}
};
for (int i = 0; i < n; i++)
cout << (classe+i)->nome << " - " << (classe+i)->media << endl;
delete[] classe; classe = nullptr;
}
5️⃣ Errori comuni (e come evitarli)
| Errore | Esempio | Soluzione |
|---|---|---|
| Memory leak (perdita di memoria) | int* p = new int(5); /* niente delete */ |
Sempre delete/delete[] quando finito. Azzera il puntatore a nullptr. |
| delete sbagliato | int* v = new int[10]; delete v; // ❌ |
Per gli array usa delete[] v; |
| Double delete | Chiamare delete due volte sullo stesso puntatore |
Dopo delete metti p = nullptr; e verifica if (p) prima di cancellare. |
| Dangling pointer | Usare *p dopo il delete |
Subito dopo il delete, imposta p = nullptr; |
| Non inizializzare | int* p; *p = 3; // ❌ |
Inizializza sempre: int* p = new int(3); oppure p = nullptr; |
6️⃣ Passare memoria dinamica alle funzioni
Le funzioni ricevono puntatori (all array o all oggetto) e possono modificarne il contenuto.
void raddoppia(int* v, int n) {
for (int i = 0; i < n; i++) v[i] *= 2;
}
Studente* migliore(Studente* v, int n) {
Studente* best = v;
for (int i = 1; i < n; i++)
if ((v+i)->media > best->media) best = (v+i);
return best; // attenzione: v deve rimanere valido nel chiamante
}
7️⃣ Nota moderna: smart pointer (anteprima)
Nel C++ moderno, per evitare errori di gestione manuale, si usano spesso gli smart pointer (es. std::unique_ptr).
Per ora lavoriamo con new/delete per capire cosa succede. In seguito potrai sostituirli con:
#include <memory>
auto p = std::make_unique<int>(42); // niente delete manuale
auto a = std::make_unique<int[]>(n); // array dinamico
LABORATORIO
Gestione Dinamica dei Posti in un Parcheggio
In questo laboratorio simulerai il funzionamento di un parcheggio intelligente che utilizza la memoria dinamica per gestire i posti disponibili.
L obiettivo è imparare ad allocare e liberare memoria nello heap usando new e delete[], evitando errori di gestione.
- Ambiente di sviluppo C++ (Code::Blocks, Dev-C++, Visual Studio Code...)
- Console per l interazione con l utente
1 Allocazione dinamica dei posti
L utente inserisce quanti posti ha il parcheggio. Il programma crea dinamicamente un array di interi per rappresentarli.
int n;
cout << "Quanti posti ha il parcheggio? ";
cin >> n;
int* posti = new int[n]; // allocazione dinamica
for (int i = 0; i < n; i++) {
posti[i] = 0; // inizialmente tutti liberi
}
2 Visualizzazione e modifica
Mostra i posti e consenti di occuparne o liberarli tramite l indice.
int scelta, indice;
do {
cout << "\n1. Visualizza posti\n2. Occupa posto\n3. Libera posto\n0. Esci\nScelta: ";
cin >> scelta;
if (scelta == 1) {
cout << "Stato posti: ";
for (int i = 0; i < n; i++) cout << posti[i] << " ";
cout << endl;
}
else if (scelta == 2) {
cout << "Numero posto da occupare: ";
cin >> indice;
if (indice >= 0 && indice < n) posti[indice] = 1;
}
else if (scelta == 3) {
cout << "Numero posto da liberare: ";
cin >> indice;
if (indice >= 0 && indice < n) posti[indice] = 0;
}
} while (scelta != 0);
3 Deallocazione della memoria
Al termine del programma libera la memoria occupata dalla struttura dinamica.
delete[] posti;
posti = nullptr;
cout << "Memoria liberata correttamente." << endl;
4 Approfondimenti
- Estendi il programma aggiungendo una funzione per contare i posti liberi e occupati.
- Gestisci un errore di input (ad esempio inserimento di un indice non valido).
- Prova a dimenticare
delete[]e osserva cosa succede (memory leak).