Язык программирования C++. Пятое издание - Стенли Липпман
Шрифт:
Интервал:
Закладка:
12.1.1. Класс shared_ptr
Подобно векторам, интеллектуальные указатели являются шаблонами (см. раздел 3.3). Поэтому при создании интеллектуального указателя следует предоставить дополнительную информацию — в данном случае тип, на который способен указывать указатель. Подобно векторам, этот тип указывают в угловых скобках, следующих за именем типа определяемого интеллектуального указателя:
shared_ptr<string> p1; // shared_ptr может указывать на строку
shared_ptr<list<int>> p2; // shared_ptr может указывать на
// список целых чисел
Инициализированный по умолчанию интеллектуальный указатель хранит нулевой указатель (см. раздел 2.3.2). Дополнительные способы инициализации интеллектуального указателя рассматриваются в разделе 12.1.3.
Интеллектуальный указатель используется теми же способами, что и обычный указатель. Обращение к значению интеллектуального указателя возвращает объект, на который он указывает. Когда интеллектуальный указатель используется в условии, результат проверки может засвидетельствовать, не является ли он нулевым:
// если указатель p1 не нулевой и не указывает на пустую строку
if (p1 && p1->empty())
*p1 = "hi"; // обратиться к значению p1, чтобы присвоить ему
// новое значение строки
Список общих функций указателей shared_ptr и unique_ptr приведен в табл. 12.1. Функции, специфические для указателя shared_ptr, перечислены в табл. 12.2.
Таблица 12.1. Функции, общие для указателей shared_ptr и unique_ptr
shared_ptr<T> sp unique_ptr<T> up Нулевой интеллектуальный указатель, способный указывать на объекты типа Т p При использовании указателя p в условии возвращается значение true, если он указывает на объект *p Обращение к значению указателя p возвращает объект, на который он указывает p->mem Синоним для (*p).mem p.get() Возвращает указатель, хранимый указателем p. Используйте его осторожно, поскольку объект, на который он указывает, может прекратить существование после удаления его интеллектуальным указателем swap(p, q) p.swap(q) Обменивает указатели в p и qТаблица 12.2. Функции, специфические для указателя shared_ptr
make_shared<T>(args) Возвращает указатель shared_ptr на динамически созданный объект типа Т. Аргументы args используются для инициализации создаваемого объекта shared_ptr<T> p(q) p — копия shared_ptr q; инкремент счетчика q. Тип содержащегося в q указателя должен быть приводим к типу Т* (см. раздел 4.11.2) p = q p и q — указатели shared_ptr, содержащие указатели, допускающие приведение друг к другу. Происходит декремент счетчика ссылок p и инкремент счетчика q; если счетчик указателя p достиг 0, память его объекта освобождается p.unique() Возвращает true, если p.use_count() равно единице, и значение false в противном случае p.use_count() Возвращает количество объектов, совместно использующих указатель p; может выполняться очень медленно, предназначена прежде всего для отладки Функция make_shared()Наиболее безопасный способ резервирования и использования динамической памяти подразумевает вызов библиотечной функции make_shared(). Она резервирует и инициализирует объект в динамической памяти, возвращая указатель типа shared_ptr на этот объект. Как и типы интеллектуальных указателей, функция make_shared() определена в заголовке memory.
При вызове функции make_shared() следует указать тип создаваемого объекта. Это подобно использованию шаблона класса — за именем функции следует указание типа в угловых скобках:
// указатель shared_ptr на объект типа int со значением 42
shared_ptr<int> p3 = make_shared<int>(42);
// р4 указывает на строку со значением '9999999999'
shared_ptr<string> р4 = make_shared<string>(10, '9');
// р5 указывает на объект типа int со значением по
// умолчанию (p. 3.3.1) 0
shared_ptr<int> р5 = make_shared<int>();
Подобно функции-члену emplace() последовательного контейнера (см. раздел 9.3.1), функция make_shared() использует свои аргументы для создания объекта заданного типа. Например, при вызове функции make_shared<string>() следует передать аргумент (аргументы), соответствующий одному из конструкторов типа string. Вызову функции make_shared<int>() можно передать любое значение, которое можно использовать для инициализации переменной типа int, и т.д. Если не передать аргументы, то объект инициализируется значением по умолчанию (см. раздел 3.3.1).
Для облегчения определения объекта, содержащего результат вызова функции make_shared(), обычно используют ключевое слово auto (см. раздел 2.5.2):
// p6 указывает на динамически созданный пустой вектор vector<string>
auto p6 = make_shared<vector<string>>();
Копирование и присвоение указателей shared_ptrПри копировании и присвоении указателей shared_ptr каждый из них отслеживает количество других указателей shared_ptr на тот же объект:
auto p = make_shared<int>(42); // объект, на который указывает p
// имеет только одного владельца
auto q(p); // p и q указывают на тот же объект
// объект, на который указывают p и q, имеет двух владельцев
С указателем shared_ptr связан счетчик, обычно называемый счетчиком ссылок (reference count). При копировании указателя shared_ptr значение счетчика увеличивается. Например, значение связанного с указателем shared_ptr счетчика увеличивается, когда он используется для инициализации другого указателя shared_ptr, а также при использовании его в качестве правого операнда присвоения, или при передаче его функции (см. раздел 6.2.1), или при возвращении из функции по значению (см. раздел 6.3.2). Значение счетчика увеличивается при присвоении нового значения указателю shared_ptr, а когда он удаляется или когда локальный указатель shared_ptr выходит из области видимости (см. раздел 6.1.1), значение счетчика уменьшается.
Как только счетчик указателя shared_ptr достигает нуля, он автоматически освобождает объект, на который указывает:
auto r = make_shared<int>(42); // объект int, на который указывает r,
// имеет одного владельца
r = q; // присвоение r переводит этот указатель на другой адрес
// приращение счетчика владельцев объекта, на который указывает q
// уменьшение счетчика владельцев объекта, на который указывает r
// объект, на который указывал r, не имеет более владельцев;
// он освобождается автоматически
Здесь резервируется переменная типа int, а ее адрес сохраняется в указателе r. Затем указателю r присваивается новое значение. В данном случае r — единственный указатель типа shared_ptr, указывающий на этот объект. В результате присвоения r = q переменная int автоматически освобождается.
Будет ли использован счетчик или другая структура данных для отслеживания количества указателей на совместно используемый объект, зависит от реализации компилятора. Главное то, что класс отслеживает количество указателей shared_ptr на тот же объект и автоматически освобождает его в подходящий момент.
Указатель shared_ptr автоматически удаляет свои объекты…Когда последний указатель shared_ptr на объект удаляется, его класс автоматически удаляет объект, на который он указывает. Для этого используется другая специальная функция-член — деструктор (destructor), аналогичная конструкторам, которые есть у каждого класса. Подобно тому, как конструктор контролирует инициализацию, деструктор контролирует происходящее при удалении объектов этого типа.