С помощью shared_ptr
unique_ptr
можно автоматически справиться с большинством объектов, память для которых выделяется динамически, не беспокоясь об утечках памяти. Следует, однако, рассмотреть один важный подводный камень. Представьте, что у нас имеются два объекта в куче, которые содержат общие указатели друг на друга, и какой-то другой общий указатель, указывающий на один из них откуда-то еще. Если этот внешний указатель выйдет за пределы области видимости, то счетчики использования обоих объектов все еще будут иметь 0
.Дополнительная информация
Рассмотрим следующий код. Допустим, вам сказали, что он провоцирует потенциальную
void function(shared_ptr, shared_ptr, int);
// "function" определена где-то еще
// ...далее по коду:
function(new A{}, new B{}, other_function());
Кто-то спросит: «Где же утечка?» — ведь объекты A
B
, для которых только что выделена память, мгновенно передаются в экземпляры типа shared_ptr
, и Да, это так, утечки памяти нам не грозят до тех пор, пока указатели хранятся в экземплярах типа shared_ptr
Когда мы вызываем функцию f(x(),y(),z())
x()
, y()
и z()
, чтобы он мог перенаправить результат их работы в функцию f
. Нас не устраивает то, что компилятор может выполнить вызовы функций x
, y
и z
в Взглянем на пример еще раз и подумаем: какие события произойдут, если компилятор решит структурировать код так, что сначала будет вызвана функция new A{}
other_function()
, а затем — B{}
, прежде чем результаты работы этих функций будут переданы далее? Если функция other_function()
сгенерирует исключение, то мы получим утечку памяти, поскольку у нас в куче все еще будет неуправляемый объект A
, поскольку мы не имели возможности передать его под управление shared_ptr
. Независимо от того, как мы обработаем исключение, дескриптор объекта Существует два простых способа обойти эту проблему:
// 1.)
function(make_shared(), make_shared(), other_function());
// 2.)
shared_ptr ap {new A{}};
shared_ptr bp {new B{}};
function(ap, bp, other_function());
Таким образом, объекты уже попадут под управление shared_ptr
Работаем со слабыми указателями на разделяемые объекты
Из примера, посвященного shared_ptr
unique_pt
r они предоставляют бесценную возможность по улучшению нашего кода, нуждающегося в управлении объектами, память для которых выделяется динамически.Копируя shared_ptr
В таких ситуациях нам поможет weak_ptr
unique_ptr
и shared_ptr
, но после прочтения этого раздела вы научитесь применять его.Как это делается
В этом примере мы реализуем программу, которая поддерживает объекты, используя экземпляры типа shared_ptr
weak_ptr
, чтобы увидеть, как это меняет поведение при управлении памятью с помощью умного указателя.1. Сначала включим необходимые заголовочные файлы и объявим об использовании пространства имен std
#include
#include
#include
using namespace std;
2. Затем реализуем класс, чей деструктор выводит на экран сообщение. Таким образом, нам будет проще проверить факт уничтожения объекта.
struct Foo {
int value;
Foo(int i) : value{i} {}
~Foo() { cout << "DTOR Foo " << value << '\n'; }
};