Condividi tramite


Durata degli oggetti e gestione delle risorse (RAII)

A differenza dei linguaggi gestiti, C++ non dispone di Garbage Collection automatica, un processo interno che rilascia memoria heap e altre risorse durante l'esecuzione di un programma. Un programma C++ è responsabile della restituzione di tutte le risorse acquisite al sistema operativo. Il mancato rilascio di una risorsa inutilizzata è detto perdita. Le risorse perse non sono disponibili per altri programmi fino all'uscita del processo. Le perdite di memoria in particolare sono una causa comune di bug nella programmazione in stile C.

C++ moderno evita di usare la memoria heap il più possibile dichiarando gli oggetti nello stack. Quando una risorsa è troppo grande per lo stack, deve essere di proprietà di un oggetto . Quando l'oggetto viene inizializzato, acquisisce la risorsa di proprietà. L'oggetto è quindi responsabile del rilascio della risorsa nel relativo distruttore. L'oggetto proprietario stesso viene dichiarato nello stack. Il principio che gli oggetti possiedono risorse è noto anche come "acquisizione delle risorse è l'inizializzazione" o RAII.

Quando un oggetto stack proprietario di risorse esce dall'ambito, viene richiamato automaticamente il distruttore. In questo modo, Garbage Collection in C++ è strettamente correlato alla durata degli oggetti ed è deterministico. Una risorsa viene sempre rilasciata in un punto noto del programma, che è possibile controllare. Solo i distruttori deterministici come quelli in C++ possono gestire equamente risorse di memoria e non di memoria.

Nell'esempio seguente viene illustrato un oggetto wsemplice . Viene dichiarata nello stack nell'ambito della funzione e viene eliminata definitivamente alla fine del blocco di funzioni. L'oggetto w non possiede risorse, ad esempio memoria allocata dall'heap. L'unico membro g è dichiarato nello stack e semplicemente esce dall'ambito insieme a w. Non è necessario alcun codice speciale nel widget distruttore.

class widget {
private:
    gadget g;   // lifetime automatically tied to enclosing object
public:
    void draw();
};

void functionUsingWidget () {
    widget w;   // lifetime automatically tied to enclosing scope
                // constructs w, including the w.g gadget member
    // ...
    w.draw();
    // ...
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety,
  // as if "finally { w.dispose(); w.g.dispose(); }"

Nell'esempio seguente è w proprietario di una risorsa di memoria e pertanto deve avere codice nel distruttore per eliminare la memoria.

class widget
{
private:
    int* data;
public:
    widget(const int size) { data = new int[size]; } // acquire
    ~widget() { delete[] data; } // release
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data member
    w.do_something();

} // automatic destruction and deallocation for w and w.data

Poiché C++11, esiste un modo migliore per scrivere l'esempio precedente: usando un puntatore intelligente dalla libreria standard. Il puntatore intelligente gestisce l'allocazione e l'eliminazione della memoria di proprietà. L'uso di un puntatore intelligente elimina la necessità di un distruttore esplicito nella widget classe .

#include <memory>
class widget
{
private:
    std::unique_ptr<int[]> data;
public:
    widget(const int size) { data = std::make_unique<int[]>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);  // lifetime automatically tied to enclosing scope
                        // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

Usando puntatori intelligenti per l'allocazione di memoria, è possibile eliminare il rischio di perdite di memoria. Questo modello funziona per altre risorse, ad esempio handle di file o socket. È possibile gestire le proprie risorse in modo analogo nelle classi. Per altre informazioni, vedere Puntatori intelligenti.

La progettazione di C++ garantisce che gli oggetti vengano eliminati definitivamente quando escono dall'ambito. Cioè, vengono distrutti quando i blocchi vengono usciti, in ordine inverso di costruzione. Quando un oggetto viene eliminato definitivamente, le basi e i membri vengono eliminati in un ordine specifico. Gli oggetti dichiarati all'esterno di qualsiasi blocco, nell'ambito globale, possono causare problemi. Può essere difficile eseguire il debug, se il costruttore di un oggetto globale genera un'eccezione.

Vedi anche

Benvenuto in C++
Informazioni di riferimento sul linguaggio C++
Libreria standard C++