Menu
Coddy logo textTech

Approfondimento sui distruttori

Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 24 di 104.

Un distruttore è una funzione membro speciale che viene eseguita automaticamente quando un oggetto viene distrutto. Il suo compito principale è rilasciare le risorse acquisite dall’oggetto durante la sua vita, come la memoria allocata dinamicamente, gli handle di file o le connessioni di rete.

Il distruttore ha lo stesso nome della classe, preceduto da una tilde ~. Non accetta parametri e non ha un tipo di ritorno:

class FileHandler {
    std::string filename;
    int* buffer;
public:
    FileHandler(std::string name, size_t size) 
        : filename(name), buffer(new int[size]) {
        std::cout << "Opening " << filename << "\n";
    }
    ~FileHandler() {
        delete[] buffer;
        std::cout << "Closing " << filename << "\n";
    }
};

I distruttori vengono chiamati automaticamente nelle seguenti situazioni:

  • Gli oggetti dello stack escono dall'ambito
  • Viene chiamato delete su un oggetto allocato nell'heap
  • La durata di vita di un oggetto temporaneo termina
void example() {
    FileHandler f1("data.txt", 100);    // Costruttore chiamato
    FileHandler* f2 = new FileHandler("log.txt", 50);
    delete f2;                           // Distruttore chiamato per f2
}   // Distruttore chiamato per f1 (esce dall'ambito)

A differenza dei costruttori, una classe può avere un solo distruttore. Non puoi sovraccaricarlo.

Se non ne definisci uno, il compilatore genera un distruttore predefinito che non fa nulla di speciale: si limita a distruggere ogni membro. Per le classi che gestiscono risorse, devi scriverne uno tuo per evitare perdite di memoria.

challenge icon

Sfida

Facile

Creiamo un gestore di sessioni che tiene traccia delle sessioni utente attive e dimostra come i distruttori liberano automaticamente le risorse quando gli oggetti vengono distrutti, sia quando escono dall'ambito sia quando vengono eliminati esplicitamente.

Creerai due file per organizzare il codice:

  • Session.h: definisci una classe Session che rappresenta una sessione utente attiva con dati allocati dinamicamente. La classe deve avere:
    • membri privati: uno username (stringa), un sessionId (int) e un puntatore a un array di interi chiamato activityLog che tiene traccia delle azioni dell'utente, insieme a un logSize per la dimensione dell'array
    • Un costruttore che accetta un nome utente, un ID sessione e la dimensione del log. Deve allocare l'array del log delle attività nell'heap, inizializzare tutti gli elementi a 0 e stampare "Session <sessionId> started for <username>"
    • Un distruttore che libera la memoria allocata e stampa "Session <sessionId> ended for <username>"
    • Un metodo logActivity(int activityCode) che memorizza il codice dell'attività nella prima posizione disponibile (tieni traccia internamente della posizione corrente); se il log è già pieno, la chiamata viene ignorata, quindi il conteggio non supera mai la dimensione del log
    • Un metodo getActivityCount() che restituisce il numero di attività registrate
    • Un metodo getUsername() che restituisce il nome utente
  • main.cpp: dimostra il comportamento del distruttore in diversi scenari. Leggi da input un nome utente e la dimensione del log (ciascuno su una riga separata), poi:
    • Crea un blocco di ambito usando parentesi graffe { } e al suo interno crea una Session allocata sullo stack con il nome utente inserito, l'ID sessione 101 e la dimensione del log inserita. Registra due attività con i codici 1 e 2, poi stampa "Stack session activities: <count>". Quando il blocco termina, il distruttore deve essere eseguito automaticamente.
    • Dopo il blocco, stampa "Stack session destroyed"
    • Crea una Session allocata nell'heap usando new, con il nome utente "Guest", l'ID sessione 202 e la dimensione del log 3. Registra un'attività con il codice 5, stampa "Heap session activities: <count>", quindi eliminala esplicitamente con delete.
    • Dopo l'eliminazione, stampa "Heap session destroyed"

Questa sfida mostra i due modi principali in cui vengono chiamati i distruttori: automaticamente quando gli oggetti allocati sullo stack escono dall'ambito e manualmente quando elimini gli oggetti allocati nell'heap. Entrambi i percorsi devono liberare correttamente il log delle attività allocato dinamicamente per evitare perdite di memoria.

Includi il file header in main.cpp usando #include "Session.h".

Provalo tu

#include <iostream>
#include <string>
#include "Session.h"

using namespace std;

int main() {
    // Leggi l'input
    string username;
    int logSize;
    getline(cin, username);
    cin >> logSize;
    
    // TODO: Crea un blocco di ambito con parentesi graffe { }
    // All'interno del blocco:
    //   - Crea un oggetto Session allocato sullo stack con lo username fornito in input, l'ID di sessione 101 e la dimensione del log fornita in input
    //   - Registra due attività con i codici 1 e 2
    //   - Stampa "Stack session activities: <count>"
    // Quando il blocco termina, il distruttore viene eseguito automaticamente
    
    // TODO: Dopo il blocco, stampa "Stack session destroyed"
    
    // TODO: Crea un oggetto Session allocato sull'heap usando new
    //   - Username: "Guest", ID di sessione: 202, dimensione del log: 3
    //   - Registra un'attività con il codice 5
    //   - Stampa "Heap session activities: <count>"
    //   - Elimina la sessione
    
    // TODO: Dopo l'eliminazione, stampa "Heap session destroyed"
    
    return 0;
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C++ online