Умный указатель

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Умный указатель (англ. smart pointer) — класс (обычно шаблонный), имитирующий интерфейс обычного указателя и добавляющий некую новую функциональность, например проверку границ при доступе или очистку памяти.

Владеющие указатели[править | править вики-текст]

Чаще всего умный указатель инкапсулирует семантику владения ресурсом. В таком случае он называется владеющим указателем.

Владеющие указатели применяются для борьбы с утечками памяти и висячими ссылками. Утечкой памяти называется ситуация, когда в программе нет ни одного указателя, хранящего адрес объекта, созданного в динамической памяти. Висячей ссылкой (англ. Dangling pointer) называется указатель, ссылающийся на уже удалённый объект. Семантика владения для динамически созданных объектов означает, что удаление или присвоение нового значения указателю будет согласовано с временем жизни объекта.

Простые владеющие указатели[править | править вики-текст]

Такие указатели при присвоении нового значения или удалении сами удаляют объект. Их недостатком являются трудности с передачей объекта за пределы области видимости указателя.

Указатели с подсчётом ссылок[править | править вики-текст]

Такие обычно используются с объектами, имеющими специальные операции «увеличить число ссылок» (AddRef() в COM) и «уменьшить число ссылок» (Release() в COM). Чаще всего такие объекты унаследованы от специального класса или интерфейса (например, IUnknown в COM).

При появлении новой ссылки на объект вызывается операция «увеличить число ссылок», а при уничтожении — «уменьшить число ссылок». Если в результате операции «уменьшить число ссылок» число ссылок на объект становится равным нулю, то объект удаляется.

Такая методика называется автоматическим подсчётом ссылок. Она согласует число указателей, хранящих адрес объекта, с числом ссылок, хранящимся в объекте, а при достижении этим числом нулевого значения приводит к удалению объекта. Её преимуществами являются относительно высокие надёжность, быстродействие и простота реализации в C++. Недостатком является усложнение использования в случае возникновения циклических ссылок (необходимость пользоваться "слабыми ссылками").

Реализации[править | править вики-текст]

В COM объекты с подсчётом ссылок реализуются следующим образом:

  • Объект обязан хранить внутри себя неотрицательное целое, которое означает число внешних указателей, ссылающихся на этот объект.
  • При присваивании указателю адреса нового объекта указатель вызывает у объекта метод AddRef(). Если перед этим указатель ссылался на другой объект, то сначала вызывается метод Release() прежнего объекта. При удалении указателя (выходе его из области видимости или разрушении объекта, полем которого он являлся), если этот указатель ссылается на существующий объект, указатель вызывает метод Release() объекта.
  • Реализация метода Release() каждый раз уменьшает число ссылок на единицу и сразу проверяет новое значение. Если число ссылок стало равно нулю, метод Release() вызывает удаление объекта.

Другой вариант реализации используется в boost::shared_ptr. В этом случае, счетчики ссылок хранятся в специальной структуре данных вне объекта.

Проблема циклических ссылок[править | править вики-текст]

Предположим, есть два объекта и в каждом из них по владеющему указателю. Указателю в первом объекте присвоим адрес второго объекта, а указателю во втором — адрес первого объекта. Если теперь всем внешним (то есть не хранящимся внутри этих объектов) указателям на два данных объекта присвоить новые значения, то указатели внутри объектов по-прежнему будут владеть друг другом и будут оставаться в памяти. В результате возникнет ситуация, когда к объектам невозможно получить доступ, то есть утечка памяти.

Проблема циклических ссылок решается либо путем соответствующего проектирования структур данных, либо использованием сборки мусора, либо использованием двух видов ссылок: сильные (владеющие) и слабые (невладеющие, напр. boost::weak_ptr).

Примеры реализаций[править | править вики-текст]

Ссылки[править | править вики-текст]