Životní cyklus objektů a správa prostředků (RAII)

Na rozdíl od spravovaných jazyků nemá jazyk C++ automatické uvolňování paměti, interní proces, který při spuštění programu uvolní paměť haldy a další prostředky. Program C++ zodpovídá za vrácení všech získaných prostředků do operačního systému. Selhání uvolnění nepoužívaného prostředku se nazývá únik. Nevracené prostředky nejsou pro ostatní programy dostupné, dokud proces neodejde. Zejména nevracení paměti jsou běžnou příčinou chyb v programování ve stylu jazyka C.

Moderní jazyk C++ zabraňuje použití paměti haldy co nejvíce tím, že deklaruje objekty v zásobníku. Pokud je prostředek pro zásobník příliš velký, měl by ho vlastnit objekt. Jakmile se objekt inicializuje, získá prostředek, který vlastní. Objekt je pak zodpovědný za uvolnění prostředku v jeho destruktoru. Vlastnící objekt je deklarován v zásobníku. Princip, kdy objekty vlastní prostředky se také označují jako inicializace získávání prostředků nebo RAII.

Když objekt zásobníku vlastněný prostředkem přestane být obor, jeho destruktor se automaticky vyvolá. Tímto způsobem uvolňování paměti v jazyce C++ úzce souvisí s životností objektu a je deterministický. Prostředek se vždy uvolní ve známém okamžiku programu, který můžete řídit. Stejně jako deterministické destruktory, jako jsou ty v jazyce C++, dokážou zpracovávat prostředky paměti a prostředků, které nejsou v paměti.

Následující příklad ukazuje jednoduchý objekt w. Je deklarován v zásobníku v oboru funkce a je zničen na konci bloku funkce. Objekt w vlastní žádné prostředky (například paměť přidělenou haldou). Jeho jediný člen g je sám deklarován v zásobníku a jednoduše jde mimo rozsah spolu s w. V widget destruktoru není potřeba žádný speciální kód.

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(); }"

V následujícím příkladu w vlastní prostředek paměti a proto musí mít kód v jeho destruktoru pro odstranění paměti.

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

Vzhledem k tomu, že C++11, existuje lepší způsob, jak napsat předchozí příklad: pomocí inteligentního ukazatele ze standardní knihovny. Inteligentní ukazatel zpracovává přidělení a odstranění paměti, která vlastní. Použití inteligentního ukazatele eliminuje potřebu explicitního destruktoru ve widget třídě.

#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

Pomocí inteligentních ukazatelů pro přidělení paměti můžete eliminovat potenciál nevracení paměti. Tento model funguje pro jiné prostředky, jako jsou popisovače souborů nebo sokety. Ve svých třídách můžete spravovat vlastní prostředky podobným způsobem. Další informace najdete v tématu Inteligentní ukazatele.

Návrh jazyka C++ zajišťuje, že objekty budou zničeny, když vyjdou mimo rozsah. To znamená, že se zničí, protože bloky jsou ukončeny, v obráceném pořadí konstrukce. Když je objekt zničen, jeho základny a členy jsou zničeny v určitém pořadí. Objekty deklarované mimo jakýkoli blok v globálním oboru můžou vést k problémům. Může být obtížné ladit, pokud konstruktor globálního objektu vyvolá výjimku.

Viz také

Vítejte zpátky v C++
Referenční dokumentace jazyka C++
Standardní knihovna C++