- Gestire il ciclo di vita degli oggetti attraverso l'implementazione di costruttori e distruttori
- Applicare la tecnica dell'overloading per fornire diverse modalità di inizializzazione
- Assicurare la corretta allocazione e deallocazione delle risorse in memoria
- Implementare logiche di inizializzazione automatica per prevenire errori di runtime
In C++, ogni oggetto ha un ciclo di vita: nasce, viene utilizzato e poi viene eliminato. Per gestire automaticamente questa vita, esistono due strumenti fondamentali: costruttori e distruttori.
1. Il Costruttore (Constructor)
Un costruttore in C++ è una funzione speciale di una classe che viene chiamata automaticamente quando un oggetto viene creato. Serve principalmente a inizializzare i membri della classe, cioè a dare valori iniziali alle variabili dell’oggetto.
È un metodo speciale che ha lo stesso nome della classe e non ha tipo di ritorno (nemmeno void).
🔧 Caratteristiche fondamentali
- Ha lo stesso nome della classe.
- Se la classe si chiama Punto, il costruttore si chiamerà Punto.
- Non ha tipo di ritorno, nemmeno void.
- Viene chiamato automaticamente quando crei un oggetto.
- Può essere sovraccaricato: puoi avere più costruttori con parametri diversi.
- Se non ne definisci uno, il compilatore ne crea uno di default.
class Punto {
private:
int x, y;
public:
// Costruttore
Punto(int a, int b) {
x = a;
y = b;
}
};
Quando
Punto p(3, 4);
il costruttore viene chiamato automaticamente e imposta x = 3 e y = 4.
💡 Senza il costruttore, dovresti impostare manualmente x e y, rischiando errori e valori casuali.
✨ Perché è importante
I costruttori rendono il codice più sicuro e leggibile, perché garantiscono che ogni oggetto inizi la sua vita in uno stato coerente. Senza di loro, dovresti ricordarti ogni volta di inizializzare tutto manualmente.
Come funziona? Il compilatore distingue i vari costruttori in base alla loro firma (signature), ovvero il numero, il tipo e l'ordine dei parametri passati. Quando crei un oggetto, il compilatore "guarda" gli argomenti che stai fornendo e richiama il costruttore che combacia esattamente con quella sequenza.
2. Il Distruttore (Destructor)
Il distruttore è il “saluto finale” di un oggetto in C++: un metodo speciale che viene eseguito automaticamente quando l’oggetto sta per essere eliminato. Se il costruttore prepara l’oggetto alla vita, il distruttore si occupa di chiudere tutto in modo ordinato.
🗑️ Cos’è un distruttore
- Ha lo stesso nome della classe, preceduto da una tilde
~. - Non ha tipo di ritorno.
- Non accetta parametri.
- Serve a liberare risorse (memoria, file aperti, connessioni).
- Viene chiamato in automatico quando l’oggetto esce di scope o viene distrutto.
“Se il costruttore inizializza l’oggetto, il distruttore fa l’opposto: si occupa di liberare le risorse quando l’oggetto non serve più.”
class Punto {
private:
int x, y;
public:
// Costruttore
Punto(int a, int b) {
x = a;
y = b;
}
// Distruttore
~Punto() {
// Qui potresti liberare risorse o stampare un messaggio
// std::cout << "Oggetto Punto distrutto\n";
}
};
x e y sono semplici tipi primitivi che vengono gestiti automaticamente. Ma la struttura è quella giusta.🧩 Esempio con risorsa da liberare
Immagina chePunto memorizzi le coordinate in un array allocato con new. In questo caso il distruttore diventa fondamentale per evitare memory leak.
class Punto {
private:
int* coord; // memoria dinamica
public:
// Costruttore
Punto(int a, int b) {
coord = new int[2]; // allocazione dinamica
coord[0] = a;
coord[1] = b;
}
// Distruttore
~Punto() {
delete[] coord; // liberazione della memoria
}
};
3. Ciclo di vita di un oggetto
Puoi pensare al ciclo così:
Creazione oggetto -> Costruttore -> Uso -> Fine scope -> Distruttore
LABORATORIO
Inizializzazione dati videogioco
In questo laboratorio creeremo una classe che gestisce i dati di un Videogioco, assicurandoci che ogni personaggio nasca con i parametri corretti.
- IDE C++
- Compilatore aggiornato
1 Implementazione della Classe con Costruttori
#include <iostream>
#include <string>
using namespace std;
class Player {
private:
string nome;
int salute;
public:
// 1. Costruttore di Default
Player() {
nome = "Ospite";
salute = 100;
cout << "[Sistema] Creato giocatore standard." << endl;
}
// 2. Costruttore con Parametri (Overloading)
Player(string n, int s) {
nome = n;
salute = s;
cout << "[Sistema] Creato giocatore personalizzato: " << nome << endl;
}
// 3. Distruttore
~Player() {
cout << "[Sistema] Il giocatore " << nome << " ha lasciato la sessione." << endl;
}
void status() {
cout << "Player: " << nome << " | Salute: " << salute << endl;
}
};
2 Test del ciclo di vita nel Main
int main() {
cout << "--- Inizio Sessione ---" << endl;
// Richiama il costruttore di default
Player p1;
p1.status();
// Richiama il costruttore con parametri
Player p2("Warrior_99", 150);
p2.status();
cout << "--- Fine Sessione ---" << endl;
return 0;
// Qui vengono chiamati automaticamente i distruttori di p2 e p1 (ordine inverso)
}
3 Esercitazione autonoma: Gestione Veicoli
Crea una classe Automobile.
- Attributi: marca, modello, litriCarburante.
- Costruttore 1: Imposta "Generica", "Base", 10 litri.
- Costruttore 2: Riceve marca e modello, imposta carburante a 20 litri.
- Distruttore: Stampa "Automobile rottamata".
- Nel main: Crea un oggetto per ogni costruttore e osserva l'ordine delle stampe.
4 Approfondimento
Cosa succede se dichiariamo un oggetto come puntatore (Player* p = new Player();)?
Il distruttore verrà chiamato solo quando useremo esplicitamente il comando delete p;. Questo è il cuore della gestione della memoria manuale in C++.