6. Поскольку общие указатели могут быть разделяемыми, они должны отслеживать количество сторон, владеющих ими. Это делается с помощью внутреннего счетчика ссылок или счетчика
use_count
. Сейчас его значение равно 1
, поскольку мы еще не копировали его. Копирование f1
в fa
увеличит значение счетчика использования до 2
. cout << "f1's use counter at " << f1.use_count() << '\n';
fa = f1;
cout << "f1's use counter at " << f1.use_count() << '\n';
7. Когда мы покинем область видимости, общие указатели f1
f2
будут уничтожены. Счетчик ссылок переменной f1
снова уменьшится до 1
, что сделает fa
единственным владельцем экземпляра типа Foo
. При разрушении f2
его счетчик ссылок будет уменьшен до 0
. В данном случае деструктор класса shared_ptr
выполнит операцию delete
для этого объекта, который удалит его. }
cout << "Back to outer scope\n";
cout << fa.use_count() << '\n';
8. Теперь вызовем функцию f
fa
. Функция f
затем выведет на экран значение счетчика ссылок, которое равно 2
. Во втором вызове f
переместим указатель в функцию. Это сделает f
единственным владельцем объекта. cout << "first f() call\n";
f(fa);
cout << "second f() call\n";
f(move(fa));
9. После того как функция f
Foo
будет мгновенно уничтожен, поскольку мы им больше не владеем. Поэтому все объекты подвергнутся уничтожению, когда отработает функция main
. cout << "end of main()\n";
}
10. Компиляция и запуск программы дадут следующий результат. Сначала мы увидим, что созданы "foo"
"bar"
. После копирования f1
(указывает на "foo"
) его счетчик ссылок увеличился до значения 2
. При выходе из области видимости "bar"
уничтожается, поскольку общий указатель был его единственным владельцем. Одна единица на экране — счетчик ссылок fa
, который является единственным владельцем "foo"
. После этого мы дважды вызываем функцию f
. При первом вызове скопировали ее в fa
, что снова увеличило его счетчик ссылок до 2
. При втором переместили его в f
, это не изменило его счетчик ссылок. Более того, поскольку функция f
к этому моменту является единственным владельцем "foo"
, объект мгновенно разрушается после того, как f
покидает область видимости. Таким образом, другие объекты кучи не разрушаются после последнего выражения print
в функции main
.$ ./shared_ptr
Inner scope begin
CTOR foo
CTOR bar
f1's use counter at 1
f1's use counter at 2
DTOR bar
Back to outer scope
1
first f()
call
f: use counter at 2
second f() call
f: use counter at 1
DTOR foo
end of main()
Как это работает
При создании и удалении объектов shared_ptr
unique_ptr
. Создание общих указателей выглядит так же, как и создание уникальных указателей (однако существует функция make_shared
, которая создает общие объекты в дополнение к функции make_unique
для уникальных указателей unique_ptr
).Основное отличие от unique_ptr
shared_ptr
, поскольку общие указатели поддерживают так называемый N
экземпляров shared_ptr
, то счетчик использования имеет значение N
. Когда экземпляр типа shared_ptr
разрушается, его деструктор уменьшает значение этого внутреннего счетчика использования. Последний общий указатель на такой объект при разрушении снизит значение счетчика использования до 0
. В данном случае будет вызван оператор delete
для объекта! Таким образом, мы не можем допустить утечку памяти, поскольку счетчик ссылок объекта отслеживается автоматически.Чтобы проиллюстрировать эту идею, взглянем на рис. 8.2.
В шаге 1 у нас имеются два экземпляра типа shared_ptr
Foo
. Значение счетчика использования установлено на 2
. Далее shared_ptr2
уничтожается, это снижает значение счетчика использования до 1
. Экземпляр Foo
пока не уничтожается, поскольку все еще существует второй общий указатель. В шаге 3
последний общий указатель также уничтожается. Это приводит к тому, что значение счетчика использования становится равным 0
. Шаг 4 выполняется сразу после шага 3. Блок управления и экземпляр типа Foo
уничтожаются, и занятая ими память возвращается в кучу.