Основные свойства умных указателей
Это просто, когда у вас есть свойства, которые вы можете назначить каждому умному указателю. Есть три важных свойства.
- вообще не владеет
- передача права собственности
- доля владения
Первый означает, что умный указатель не может удалить объект, потому что он ему не принадлежит. Второе означает, что только один умный указатель может одновременно указывать на один и тот же объект. Если интеллектуальный указатель должен быть возвращен из функций, владение передается, например, возвращенному интеллектуальному указателю.
Третий означает, что несколько интеллектуальных указателей могут указывать на один и тот же объект одновременно. Это относится и к необработанному указателю , однако необработанным указателям не хватает важной функции: они не определяют, владеют ли они или нет. Интеллектуальный указатель доли владения удалит объект, если каждый владелец откажется от объекта. Такое поведение часто требуется, поэтому широко распространены умные указатели с общим доступом.
Некоторые владельцы умных указателей не поддерживают ни второе, ни третье. Поэтому они не могут быть возвращены из функций или переданы куда-либо еще. Что наиболее подходит для RAII
целей, где интеллектуальный указатель хранится локально и просто создается, чтобы освободить объект после того, как он выходит из области видимости.
Доля владения может быть реализована с помощью конструктора копирования. Это естественно копирует умный указатель, и копия, и оригинал будут ссылаться на один и тот же объект. Передача права собственности в настоящее время не может быть реализована в C ++, потому что нет средств для передачи чего-либо из одного объекта в другой, поддерживаемых языком: если вы пытаетесь вернуть объект из функции, происходит то, что объект копируется. Таким образом, умный указатель, который реализует передачу права собственности, должен использовать конструктор копирования для реализации этой передачи права собственности. Однако это, в свою очередь, нарушает его использование в контейнерах, поскольку требования определяют определенное поведение конструктора копирования элементов контейнеров, которое несовместимо с так называемым поведением «движущегося конструктора» этих интеллектуальных указателей.
C ++ 1x обеспечивает встроенную поддержку передачи права собственности, вводя так называемые «конструкторы перемещения» и «операторы назначения перемещения». Он также поставляется с таким интеллектуальным указателем передачи права собственности, который называется unique_ptr
.
Категоризация умных указателей
scoped_ptr
- это умный указатель, который не может быть передан или разделен. Это просто полезно, если вам нужно локально выделить память, но убедитесь, что она снова освобождается, когда выходит из области видимости. Но он все еще может быть заменен другим scoped_ptr, если вы хотите это сделать.
shared_ptr
- это умный указатель, который разделяет владение (третий вид выше). Он подсчитывает ссылки, поэтому он может видеть, когда последняя его копия выходит из области видимости, а затем освобождает управляемый объект.
weak_ptr
- не принадлежащий интеллектуальный указатель. Он используется для ссылки на управляемый объект (управляемый shared_ptr) без добавления счетчика ссылок. Обычно вам нужно получить необработанный указатель из shared_ptr и скопировать его. Но это было бы небезопасно, поскольку у вас не было бы способа проверить, когда объект был фактически удален. Таким образом, weak_ptr предоставляет средства, ссылаясь на объект, управляемый shared_ptr. Если вам нужен доступ к объекту, вы можете заблокировать управление им (чтобы избежать того, что в другом потоке shared_ptr освобождает его при использовании объекта), а затем использовать его. Если слабый_птр указывает на уже удаленный объект, он заметит вас, выдав исключение. Использование weak_ptr наиболее полезно, когда у вас есть циклическая ссылка: подсчет ссылок не может легко справиться с такой ситуацией.
intrusive_ptr
похож на shared_ptr, но он не сохраняет счетчик ссылок в shared_ptr, но оставляет увеличение / уменьшение счетчика некоторым вспомогательным функциям, которые должны быть определены управляемым объектом. Это имеет преимущество в том, что уже ссылочный объект (который имеет счетчик ссылок, увеличенный с помощью внешнего механизма подсчета ссылок) может быть вставлен в intrusive_ptr - потому что счетчик ссылок больше не является внутренним для интеллектуального указателя, но интеллектуальный указатель использует существующий механизм подсчета ссылок.
unique_ptr
- указатель передачи права собственности. Вы не можете скопировать его, но вы можете переместить его с помощью конструкторов перемещения C ++ 1x:
unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!
Это семантика, которой подчиняется std :: auto_ptr, но из-за отсутствия собственной поддержки перемещения он не может обеспечить их без ошибок. unique_ptr автоматически украдет ресурсы из временного другого unique_ptr, который является одной из ключевых особенностей семантики перемещения. auto_ptr будет устаревшим в следующем выпуске C ++ Standard в пользу unique_ptr. C ++ 1x также позволит вставлять объекты, которые являются только подвижными, но не копируемыми в контейнеры. Так что вы можете, например, втиснуть unique_ptr в вектор. Я остановлюсь здесь и отсылаю вас к прекрасной статье об этом, если вы хотите узнать больше об этом.