Управление временем жизни и ресурсами объекта (RAII)

В отличие от управляемых языков, C++ не имеет автоматической сборки мусора, внутренний процесс, который освобождает кучи памяти и другие ресурсы в процессе выполнения программы. Программа C++ отвечает за возврат всех приобретенных ресурсов в операционную систему. Неиспользуемый ресурс называется утечкой. Утечка ресурсов недоступна другим программам до тех пор, пока процесс не завершится. Утечки памяти, в частности, являются распространенными причинами ошибок в программировании в стиле C.

Современный C++ избегает использования памяти кучи как можно больше, объявляя объекты в стеке. Если ресурс слишком велик для стека, он должен принадлежать объекту. По мере инициализации объекта он получает принадлежащий ему ресурс. Затем объект отвечает за освобождение ресурса в деструкторе. Сам объект-владельцев объявляется в стеке. Принцип, в котором объекты имеют собственные ресурсы, также называются инициализацией ресурсов или RAII.

Когда объект стека, принадлежащий ресурсу, выходит из область, его деструктор автоматически вызывается. Таким образом, сборка мусора в C++ тесно связана со временем существования объекта и детерминирована. Ресурс всегда освобождается в известной точке программы, которую можно контролировать. Детерминированные деструкторы, такие как в C++, могут обрабатывать ресурсы памяти и не в памяти одинаково.

В следующем примере показан простой объект w. Он объявляется в стеке в область функции и уничтожается в конце блока функций. Объект w не владеет ресурсами (например, выделенной кучей памяти). Его единственный член g объявляется на стеке, и просто выходит из область вместе с w. В деструкторе не требуется специальный widget код.

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

В следующем примере w принадлежит ресурс памяти и поэтому должен иметь код в деструкторе для удаления памяти.

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

Так как C++11 лучше написать предыдущий пример: с помощью интеллектуального указателя из стандартной библиотеки. Интеллектуальный указатель обрабатывает выделение и удаление памяти, принадлежащей ей. Использование интеллектуального указателя устраняет необходимость явного деструктора в widget классе.

#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

Используя интеллектуальные указатели для выделения памяти, можно исключить возможность утечки памяти. Эта модель работает для других ресурсов, таких как дескрипторы файлов или сокеты. Вы можете управлять собственными ресурсами аналогичным образом в классах. Дополнительные сведения см. в разделе "Умные указатели".

Конструкция C++ гарантирует, что объекты уничтожаются при выходе из область. То есть они уничтожаются по мере выхода блоков в обратном порядке строительства. При уничтожении объекта его базы и члены уничтожаются в определенном порядке. Объекты, объявленные вне любого блока, в глобальном область, могут привести к проблемам. Может быть трудно выполнить отладку, если конструктор глобального объекта создает исключение.

См. также

Возвращение к C++
Справочник по языку C++
Стандартная библиотека C++