DTOR unique Foo instance
DTOR shared Foo instance
Как это работает
Обычно unique_ptr
shared_ptr
просто вызывают оператор delete
для внутренних указателей, когда должны уничтожить объект, который сопровождают. В этом разделе мы создали класс, для которого нельзя выделить память, используя x = new Foo{123}
, и разрушить объект непосредственно с помощью delete x
.Функция Foo::create_foo
Foo
, и это не вызывает других проблем, поскольку умные указатели работают с необработанными.Сложность заключается в том, что нужно научить классы unique_ptr
shared_ptr
С этой точки зрения оба типа умных указателей несколько отличаются друг от друга. Чтобы определить пользовательскую функцию удаления для unique_ptr
delete
класса Foo
— void Foo::destroy_foo(Foo*);
, типом уникального указателя, сопровождающего экземпляр типа Foo
, должен быть unique_ptr
. Теперь он может хранить указатель на функцию destroy_foo
, которую мы предоставляем в качестве второго параметра конструктора в нашей функции make_unique_foo
.Если передача пользовательской функции удаления для класса unique_ptr
shared_ptr
, shared_ptr
. Почему это не может быть так же просто и для типа unique_ptr
?Почему так просто передать экземпляру класса shared_ptr
delete
, не изменяя типа общего указателя? Причина кроется в природе общих указателей, поддерживающих блок управления. Блок управления общих указателей — объект, имеющий виртуальные функции. Это значит, что блок управления обычного общего указателя и блок управления общего указателя с пользовательским delete
различаются! Чтобы с помощью уникального указателя применить пользовательскую функцию удаления, нужно изменить тип этого указателя. Если мы хотим, чтобы общий указатель задействовал пользовательскую функцию удаления, то это также изменит тип внутреннего Описанный прием можно применить и для уникальных указателей, но в таком случае он повлечет некоторые издержки во время выполнения программы. Это не то, что мы хотим, поскольку уникальные указатели не должны создавать лишних издержек.
Открываем доступ к разным переменным — членам одного объекта
Представим, что у нас есть общий указатель на некий сложный объект, память для которого выделяется динамически. Нужно создать новый поток, выполняющий какую-то продолжительную работу для одного из членов этого сложного объекта. Если мы хотим освободить этот общий указатель сейчас, то объект будет удален, хотя другие потоки все еще могут пытаться получить к нему доступ. Если же мы не хотим давать объекту потока указатель на весь объект, поскольку данное действие пересечется с нашим «аккуратным» интерфейсом или по каким-то другим причинам, то значит ли это, что придется управлять памятью вручную?
Нет. Вы можете использовать общие указатели, которые, с одной стороны, ссылаются на член крупного общего объекта, а с другой — выполняют автоматическое управление памятью для всего исходного объекта.
В данном разделе мы создадим подобный сценарий (без потоков, чтобы не усложнять задачу) с целью ознакомиться с этой удобной функцией типа shared_ptr
Как это делается
В этом примере мы определим структуру, которая состоит из нескольких членов. Далее выделим память для экземпляра структуры в куче, ее будет сопровождать общий указатель. Из него мы получим больше общих указателей, указывающих не на сам объект, а на его члены.
1. Сначала включим необходимые заголовочные файлы, а затем объявим об использовании пространства имен std
#include
#include
#include
using namespace std;
2. Далее определим класс, который имеет разные члены. Позволим общим указателям указывать на отдельные члены. Чтобы увидеть, когда класс создается и уничтожается, будет выводить сообщения на экран в конструкторе и деструкторе.
struct person {
string name;
size_t age;
person(string n, size_t a)
: name{move(n)}, age{a}
{ cout << "CTOR " << name << '\n'; }
~person() { cout << "DTOR " << name << '\n'; }
};