Параметр функции process() передается по значению, поэтому аргумент копируется в параметр ptr. Копирование указателя shared_ptr осуществляет инкремент его счетчика ссылок. Таким образом, в функции process() значение счетчика не меньше 2. По завершении функции process() осуществляется декремент счетчика ссылок указателя ptr, но он не может достигнуть нуля. Поэтому, когда локальная переменная ptr удаляется, память, на которую она указывает, не освобождается.
Правильный способ использования этой функции подразумевает передачу ей указателя shared_ptr:
shared_ptr
process(p); //
//
int i = *p; //
Хотя функции process() нельзя передать встроенный указатель, ей можно передать временный указатель shared_ptr, явно созданный из встроенного указателя. Но это, вероятно, будет ошибкой:
int *x(new int(1024)); //
//
//
process(shared_ptr
int j = *x; //
В этом вызове функции process() передан временный указатель shared_ptr. Этот временный указатель удаляется, когда завершается выражение, в котором присутствует вызов. Удаление временного объекта приводит к декременту счетчика ссылок, доводя его до нуля. Память, на которую указывает временный указатель, освобождается при удалении временного указателя.
Но указатель x продолжает указывать на эту (освобожденную) область памяти; теперь x — потерянный указатель. Результат попытки использования значения, на которое указывает указатель x, непредсказуем.
При связывании указателя shared_ptr с простым указателем ответственность за эту память передается указателю shared_ptr. Как только ответственность за область памяти встроенного указателя передается указателю shared_ptr, больше нельзя использовать встроенный указатель для доступа к памяти, на которую теперь указывает указатель shared_ptr.
shared_ptrКласс shared_ptr предоставляет также несколько других операций, перечисленных в табл. 12.2 и табл. 12.3. Чтобы присвоить новый указатель указателю shared_ptr, можно использовать функцию reset():
p = new int(1024); //
//
p.reset(new int(1024)); //
Подобно оператору присвоения, функция reset() модифицирует счетчики ссылок, а если нужно, удаляет объект, на который указывает указатель p. Функцию-член reset() зачастую используют вместе с функцией unique() для контроля совместного использования объекта несколькими указателями shared_ptr. Прежде чем изменять базовый объект, проверяем, является ли владелец единственным. В противном случае перед изменением создается новая копия:
if (!p.unique())
p.reset(new string(*p)); //
*p += newVal; //
//
Упражнение 12.10. Укажите, правилен ли следующий вызов функции process(), определенной в текущем разделе. В противном случае укажите, как его исправить?
shared_ptr
process(shared_ptr
Упражнение 12.11. Что будет, если вызвать функцию process() следующим образом?
process(shared_ptr
Упражнение 12.12. Используя объявления указателей p и sp, объясните каждый из следующих вызовов функции process(). Если вызов корректен, объясните, что он делает. Если вызов некорректен, объясните почему:
auto p = new int();
auto sp = make_shared
(a) process(sp);
(b) process(new int());
(c) process(p);