Gestisci la memoria in modo efficiente ed evita memory leak.
I puntatori e la gestione dinamica della memoria sono concetti avanzati ma fondamentali in C++ per scrivere programmi efficienti e flessibili.
1. Puntatori e Riferimenti
📌 Cosa sono i puntatori? Variabili che contengono indirizzi di memoria di altre variabili. Permettono di manipolare direttamente la memoria.
1.1 Dichiarazione e Operatori
Dereferenziazione
Accede al valore puntato
*ptr
Indirizzo
Restituisce l'indirizzo di una variabile
&variabile
Freccia
Accede a membri tramite puntatore
ptr->membro
#include <iostream>
using namespace std;
int main() {
int variabile = 10;
int *ptr = &variabile; // ptr contiene l'indirizzo di variabile
cout << "Valore di variabile: " << variabile << endl;
cout << "Indirizzo di variabile: " << &variabile << endl;
cout << "Valore memorizzato in ptr: " << ptr << endl;
cout << "Valore puntato da ptr: " << *ptr << endl;
// Modifica tramite puntatore
*ptr = 20;
cout << "Nuovo valore di variabile: " << variabile << endl;
return 0;
}
1.2 Differenza tra Puntatori e Riferimenti
P Puntatori
Riassegnabili: Sì
Nullabile: Sì (
nullptr)Sintassi:
*ptr,&variabile
int *ptr = &x;
R Riferimenti
Riassegnabili: No
Nullabile: No
Sintassi:
int &ref = x;
int &ref = x;
#include <iostream>
using namespace std;
int main() {
int variabile = 20;
int &riferimento = variabile; // riferimento a variabile
cout << "Valore di variabile: " << variabile << endl;
cout << "Valore tramite riferimento: " << riferimento << endl;
riferimento = 30; // modifica anche la variabile originale
cout << "Nuovo valore di variabile: " << variabile << endl;
// I riferimenti non possono essere riassegnati
int altraVariabile = 40;
// riferimento = altraVariabile; // Questo modifica il valore, non il riferimento
return 0;
}
2. Allocazione Dinamica della Memoria
📌 Perché usare l'allocazione dinamica? Quando non conosci a priori la quantità di memoria necessaria durante la compilazione.
2.1 Operatori new e delete
new - Allocazione
Singolo elemento:
new tipoArray:
new tipo[dimensione]
int *ptr = new int;
delete - Deallocazione
Singolo elemento:
delete ptrArray:
delete[] array
delete ptr;
2.2 Esempio: Allocazione Singolo Elemento
#include <iostream>
using namespace std;
int main() {
// Allocazione dinamica di un singolo intero
int *ptr = new int;
// Assegnazione del valore
*ptr = 42;
cout << "Valore allocato dinamicamente: " << *ptr << endl;
cout << "Indirizzo di memoria: " << ptr << endl;
// DEVE essere deallocato!
delete ptr;
ptr = nullptr; // Buona pratica
return 0;
}
2.3 Esempio: Array Dinamico
#include <iostream>
using namespace std;
int main() {
int dimensione;
cout << "Inserisci la dimensione dell'array: ";
cin >> dimensione;
// Allocazione dinamica dell'array
int *array = new int[dimensione];
// Inizializzazione
for (int i = 0; i < dimensione; i++) {
array[i] = i * 2;
}
cout << "Contenuto dell'array:" << endl;
for (int i = 0; i < dimensione; i++) {
cout << "Elemento " << i << ": " << array[i] << endl;
}
// DEVE essere deallocato con delete[]
delete[] array;
array = nullptr;
return 0;
}
⚠️ ATTENZIONE: Usare
deleteper array odelete[]per singoli elementi causa comportamenti indefiniti!
3. Best Practices
Regole fondamentali:
Inizializza sempre
Assicurati che ogni puntatore sia inizializzato (preferibilmente a nullptr)
Dealloca sempre
Usa delete o delete[] per liberare la memoria non più necessaria
Evita doppia delete
Deallocare la stessa memoria più volte causa comportamenti indefiniti
Usa riferimenti quando possibile
Più semplici e meno soggetti a errori rispetto ai puntatori
Smart Pointer (C++11+)
Preferisci std::unique_ptr e std::shared_ptr per gestione automatica
3.1 Esempio: Gestione Sicura
#include <iostream>
using namespace std;
int main() {
int *ptr = nullptr; // Inizializzazione sicura
// Allocazione con controllo
ptr = new int;
if (ptr == nullptr) {
cout << "Errore di allocazione!" << endl;
return -1;
}
*ptr = 100;
cout << "Valore: " << *ptr << endl;
// Deallocazione sicura
if (ptr != nullptr) {
delete ptr;
ptr = nullptr; // Importante!
}
return 0;
}
Esercizi Pratici
Metti in pratica la gestione dinamica della memoria
Esercizio 1: Gestione Array Dinamico
Obiettivo: Creare e gestire un array dinamico con input utente.
#include <iostream>
using namespace std;
int main() {
int dimensione;
cout << "Inserisci la dimensione dell'array: ";
cin >> dimensione;
// Allocazione dinamica
int *array = new int[dimensione];
// Inserimento elementi
cout << "Inserisci " << dimensione << " numeri interi:" << endl;
for (int i = 0; i < dimensione; i++) {
cout << "Elemento " << i << ": ";
cin >> array[i];
}
// Calcolo somma e media
int somma = 0;
for (int i = 0; i < dimensione; i++) {
somma += array[i];
}
double media = static_cast<double>(somma) / dimensione;
cout << "Somma: " << somma << endl;
cout << "Media: " << media << endl;
// Trova massimo e minimo
int max = array[0], min = array[0];
for (int i = 1; i < dimensione; i++) {
if (array[i] > max) max = array[i];
if (array[i] < min) min = array[i];
}
cout << "Valore massimo: " << max << endl;
cout << "Valore minimo: " << min << endl;
// Deallocazione
delete[] array;
array = nullptr;
return 0;
}
Esercizio 2: Scambio con Puntatori
Obiettivo: Scambiare valori usando puntatori.
#include <iostream>
using namespace std;
// Versione con puntatori
void scambiaPuntatori(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
// Versione con riferimenti (alternativa)
void scambiaRiferimenti(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 10, y = 20;
cout << "Prima dello scambio: x = " << x << ", y = " << y << endl;
// Scambio con puntatori
scambiaPuntatori(&x, &y);
cout << "Dopo scambio con puntatori: x = " << x << ", y = " << y << endl;
// Scambio con riferimenti
scambiaRiferimenti(x, y);
cout << "Dopo scambio con riferimenti: x = " << x << ", y = " << y << endl;
return 0;
}
5. Esercizi Avanzati
Esercizio 3: Matrice Dinamica
Obiettivo: Creare e gestire una matrice 2D dinamica.
#include <iostream>
using namespace std;
int main() {
int righe, colonne;
cout << "Inserisci numero di righe: ";
cin >> righe;
cout << "Inserisci numero di colonne: ";
cin >> colonne;
// Allocazione matrice 2D
int **matrice = new int*[righe];
for (int i = 0; i < righe; i++) {
matrice[i] = new int[colonne];
}
// Inizializzazione
int contatore = 1;
for (int i = 0; i < righe; i++) {
for (int j = 0; j < colonne; j++) {
matrice[i][j] = contatore++;
}
}
// Stampa matrice
cout << "\\nMatrice:" << endl;
for (int i = 0; i < righe; i++) {
for (int j = 0; j < colonne; j++) {
cout << matrice[i][j] << "\\t";
}
cout << endl;
}
// Calcolo somma righe
cout << "\\nSomma per riga:" << endl;
for (int i = 0; i < righe; i++) {
int sommaRiga = 0;
for (int j = 0; j < colonne; j++) {
sommaRiga += matrice[i][j];
}
cout << "Riga " << i << ": " << sommaRiga << endl;
}
// Deallocazione (IMPORTANTE: in ordine inverso)
for (int i = 0; i < righe; i++) {
delete[] matrice[i];
}
delete[] matrice;
matrice = nullptr;
return 0;
}
Esercizio 4: Gestione Stringhe Dinamiche
Obiettivo: Manipolare stringhe con allocazione dinamica.
#include <iostream>
#include <cstring>
using namespace std;
char* copiaStringa(const char* originale) {
if (originale == nullptr) return nullptr;
int lunghezza = strlen(originale);
char* copia = new char[lunghezza + 1]; // +1 per il terminatore
strcpy(copia, originale);
return copia;
}
char* concatenaStringhe(const char* str1, const char* str2) {
if (str1 == nullptr || str2 == nullptr) return nullptr;
int len1 = strlen(str1);
int len2 = strlen(str2);
char* risultato = new char[len1 + len2 + 1];
strcpy(risultato, str1);
strcat(risultato, str2);
return risultato;
}
int main() {
const char* originale = "Ciao Mondo";
// Copia dinamica
char* copia = copiaStringa(originale);
cout << "Stringa originale: " << originale << endl;
cout << "Copia dinamica: " << copia << endl;
// Concatenazione dinamica
char* concatenata = concatenaStringhe("Hello ", "World!");
cout << "Stringa concatenata: " << concatenata << endl;
// DEALLOCARE tutto!
delete[] copia;
delete[] concatenata;
return 0;
}
Conclusioni
Punti chiave da ricordare:
Puntatori vs Riferimenti
Puntatori: flessibili ma complessi. Riferimenti: semplici ma vincolati
Allocazione Dinamica
new/deleteper memoria flessibile, fondamentale per array di dimensione sconosciutaGestione Sicura
Sempre inizializzare, sempre deallocare, usare
nullptr, evitare doppie deleteBest Practices
Preferire riferimenti quando possibile, considerare smart pointer in C++ moderno
I puntatori e la gestione dinamica della memoria offrono un controllo granulare sulle risorse del computer. Con una comprensione solida di questi concetti, sarai in grado di scrivere codice più efficiente, modulare e sicuro. Ricorda: con grandi poteri (puntatori) derivano grandi responsabilità (gestione memoria)! 🚀
Esercitazioni Pratiche C++
Metti in pratica ciò che hai imparato con queste esercitazioni complete
Tutte le Esercitazioni
12 esercitazioni complete per padroneggiare il C++