{
if (!p) { return; }
cout << "Processing " << p->name << '\n';
}
4. В функции main откроем еще одну область видимости, создадим два объекта типа Foo
unique_ptr
, p1
. Создадим уникальный указатель p2
путем вызова make_unique
с аргументами, которые в противном случае передали бы конструктору класса Foo
. Этот способ более элегантен, поскольку можно воспользоваться автоматическим определением типов и при первом обращении к объекту он уже будет управляться unique_ptr
:int main()
{
{
unique_ptr
auto p2 (make_unique
}
5. После того как мы покинем область видимости, оба объекта будут разрушены и их память вернется в кучу. Взглянем на функцию process_item
unique_ptr
. Если мы создадим новый экземпляр типа Foo
, управляемый unique_ptr
в вызове функции, то его время жизни будет ограничено областью видимости функции. Когда функция process_item
отработает, объект будет уничтожен: process_item(make_unique
6. Если мы хотим вызвать process_item
unique_ptr
по значению; это значит, что его вызов создаст копию. Но unique_ptr
нельзя скопировать, его можно только Foo
и переместим один из них в process_item
. Взглянув на консоль, мы увидим, что foo2
был уничтожен после того, как отработала функция process_item
, поскольку мы передали ей право владения. Объект foo3
продолжит существовать до тех пор, пока не отработает функция main
. auto p1 (make_unique
auto p2 (make_unique
process_item(move(p1));
cout << "End of main()\n";
}
7. Скомпилируем и запустим программу. Сначала мы увидим вызовы конструктора и деструктора для foo
bar
. Они разрушаются после того, как программа покидает дополнительную область видимости. Обратите внимание: объекты разрушаются в порядке, обратном тому, в котором были созданы. Следующая строка конструктора принадлежит foo1
— мы создали этот объект во время вызова process_item
. Он уничтожается сразу после вызова функции. Затем создали объекты foo2
и foo3
. Первый из них уничтожается сразу после вызова process_item
, где мы передали право владения. Другой элемент, foo3
, разрушается после выполнения последней строки кода функции main
.$ ./unique_ptr
CTOR foo
CTOR bar
DTOR bar
DTOR foo
CTOR foo1
Processing foo1
DTOR foo1
CTOR foo2
CTOR foo3
Processing foo2
DTOR foo2
End of main()
DTOR foo3
Как это работает
Управлять объектами кучи с помощью std::unique_ptr
Если мы присвоим какой-то новый указатель уникальному указателю, то он сначала удалит старый объект, а только затем сохранит новый указатель. Для переменной уникального указателя x
x.reset()
только затем, чтобы удалить объект, на который он указывает, не присваивая новый указатель. Еще одна эквивалентная альтернатива повторному присваиванию с помощью x = new_pointer
— это x.reset(new_pointer)
. unique_ptr
release
, но использовать ее в большинстве ситуаций не рекомендуется.Поскольку указатели нужно проверять перед разыменованием, они переопределяют некоторые операторы так, чтобы те походили на необработанные указатели. Условия наподобие if (p) {...}
if (p != nullptr) {...}
работают так же, как если бы мы проверяли необработанный указатель.Разыменовать уникальный указатель можно с помощью функции get()
operator*
, что опять же делает их похожими на необработанные указатели.