Некоторые вопросы о shared_ptr, scoped_ptr и shared_array
У меня есть несколько вопросов о интеллектуальных указателях, реализованных в библиотеке надстроек. Является ли единственная разница между shared_ptr и scoped_ptr в том, что scoped_ptr не имеет конструктора копирования, а shared_ptr имеет его? Должен ли я использовать тогда scoped_ptr вместо shared_ptr всегда, когда объект не вызывает конструктор копирования? Я также не понимаю идею общего / объемного массива. Разве я не могу использовать вместо него std::vector?
5 ответов
Является ли единственная разница между shared_ptr и scoped_ptr в том, что scoped_ptr не имеет конструктора копирования, а shared_ptr имеет его?
Разница более фундаментальная, чем эта; это связано с тем, как умные указатели владеют объектом, на который он указывает. Что отличает умные указатели от тупых указателей, так это то, что концепция владения является ключевым компонентом их функций. Семантика владения - это то, что отличает разные виды умных указателей.
Поскольку умные указатели "владеют" тем, на что они указывают, они могут делать полезные вещи, такие как удаление объектов, когда умные указатели исчезают (это стало возможным благодаря использованию только правил языка; никакой магии компилятора не требуется). Таким образом, управление памятью в C++ может быть почти автоматическим (несмотря на все заявления об обратном, в современном C++ очень мало ручного управления памятью).
shared_ptr
реализует семантику подсчета ссылок для управления памятью. множественныйshared_ptr
Может иметь один объект.shared_ptr
уход не обязательно удаляет объект, на который он указывает, потому что может быть другойshared_ptr
владеющий объектом. Последнийshared_ptr
который владеет объектом и уходит, удалит принадлежащий ему объект.scoped_ptr
реализует семантику исключительной собственности. Только одинscoped_ptr
может владеть любым одним объектом. Когдаscoped_ptr
Уходит, он всегда удаляет принадлежащий ему объект (потому что есть только один владелец). Обычно он используется в качестве облегченного механизма RAII для объектов, размещенных в бесплатном хранилище.
Версии массива (shared_array
а также scoped_array
) имеют практически одинаковую семантику, но предназначены специально для массивов, например используют delete[]
вместо delete
, реализует оператор индекса массива и т. д.
shared_ptr
а также shared_array
также позволяет указать пользовательский удалитель, если по умолчанию delete
поведение не подходит для объекта. scoped_ptr
а также scoped_array
не имеют такой способности, так как они довольно легкие по сравнению с shared_ptr
а также shared_array
,
В C++11, новейшей и текущей версии C++, есть также unique_ptr
, который так же, как scoped_ptr
кроме того, что вы можете передать право собственности на объект другому unique_ptr
, В C++03, более старой, но более широко поддерживаемой версии C++, есть auto_ptr
что эквивалентно unique_ptr
за исключением того, что было легко использовать его небезопасным образом случайно (именно поэтому он устарел в C++11).
Должен ли я использовать тогда scoped_ptr вместо shared_ptr всегда, когда объект не вызывает конструктор копирования?
Какой из них вы выберете, не зависит от наличия конструктора копирования, так как shared_ptr
а также scoped_ptr
не требует, чтобы объект был копируемым. Вы выбираете один в зависимости от требуемой семантики владения. Если у объекта будет несколько владельцев, вы используете shared_ptr
, Если у объекта будет только один владелец и существование объекта длится только в пределах области действия, используйте scoped_ptr
(отсюда и название scoped_ptr
).
Я также не понимаю идею общего / объемного массива. Разве я не могу использовать вместо него std::vector?
std::vector
не реализует семантику подсчета ссылок, такую как shared_array
делает. std::vector
больше похоже scoped_array
, но можно скопировать. Когда вы копируете std::vector
Вы также копируете все содержащиеся в нем элементы. Это не так для scoped_array
, std::vector
также есть функции, которые позволяют вам манипулировать и исследовать его содержимое (например, push_back
, insert
, erase
и т. д.), но гораздо более тяжелый, чем scoped_array
,
Я бы сказал, что вы думаете об этом неправильно. Вопрос не в том , вызываете ли вы конструктор копирования, а в том, нужно ли вам вызывать конструктор копирования. Другими словами, вы должны решить, использовать ли shared_ptr или scoped_ptr не на основе чтения вашего кода, а на основе размышлений о принадлежности объекта и времени его жизни.
Скажем, у вас есть объект, который вы хотите создать в куче, а не в стеке, по любой причине (может быть, он слишком большой, чтобы быть в стеке, может быть, вы захотите заменить его другим объектом в какой-то момент, может быть, вы хочу, чтобы он был инициализирован поздно), но его время жизни никогда не должно превышать объем, в котором он находится. Типичным примером этого являются переменные экземпляра в классе: их часто следует удалять при удалении объекта, в котором они находятся. Затем вы должны использовать scoped_ptr.
Но иногда вы не знаете, когда объект будет безопасно удалить заранее. Например, рассмотрим объект в таблице поиска. Вы хотите вернуть его в ответ на поиск, но что произойдет, когда он будет удален - может ли кто-то еще использовать объект, который просматривал его ранее? В подобных случаях вы можете использовать shared_ptr, который разделяет владение объектами, так что он удаляется только тогда, когда у кого-то больше нет копии указателя.
Так почему же кто-то использует scoped_ptr вместо shared_ptr? Прежде всего, знание того, когда вызывается деструктор, является одним из больших преимуществ неуправляемых языков, таких как C++. Может быть, деструктор дорог, или, может быть, он освобождает ресурс; приятно знать, когда такие вещи случаются. Кроме того, с shared_ptr существует вероятность утечек памяти, если вы случайно создадите циклическую ссылку.
В целом, почти каждый указатель, который у вас есть, должен быть "принадлежащим" - в вашем коде должно быть одно место, которое уведомляет и удаляет его. scoped_ptr отлично подходит для этого; когда вы хотите, чтобы принадлежащий объект передавался не владельцам, вы можете использовать пустой указатель. Но если вам абсолютно необходимо владение объектом для совместного использования, используйте shared_ptr - при условии, что вы используете его правильно!
Что касается объединенных / совместно используемых массивов, вы, безусловно, можете использовать std::vector, но массивы дешевле, и иногда люди хотят выиграть в производительности.
Да. scoped_ptr
не позволяет копировать, пока shared_ptr
делает. Но эта "простая" разница оказывает огромное влияние как на реализацию, так и на использование интеллектуального указателя.
scoped_ptr
быстрее и легче, чем shared_ptr
потому что подсчет ссылок не участвует. shared_ptr
будет подсчитывать количество назначений и не удалит объект, пока все ссылки не истекли / не вышли из области видимости.
Теперь ваш вопрос относительно векторов подразумевает, что вы на самом деле не знакомы с концепцией динамического распределения и разницы между этим и статическим распределением на статическом. Вы действительно должны прочитать учебник по C(++) и выяснить причины динамического выделения и когда это необходимо.
Вектор хранит список объектов. XXX_ptr
хранит указатель на (один) динамически размещенный объект. Яблоки и апельсины.
Если вы выделите память, вы можете поместить вновь созданный указатель в указатель с областью видимости, чтобы в случае сбоя malloc/noew память была освобождена. Это то, как я обычно использую его, или если это объект, который должен быть размещен в куче, но я хочу рассматривать его как объект стека с точки зрения того, что он будет живым только до конца области действия.
Общий указатель - это если вы хотите передать указатель и позволить объекту иметь нескольких владельцев. Тогда никому из владельцев не нужно брать на себя ответственность за объект, и все они могут просто прекратить его использование и быть уверенным, что он будет освобожден от ошибок. (вы не хотите освобождать объект, который, как вы знаете, используется кем-то другим)
shared_ptr
сильно отличается от scoped_ptr
, scoped_ptr
(который теперь стандартизирован в C++11 как std::unique_ptr
) - это просто интеллектуальный указатель в стиле RAII, который берет на себя владение ресурсом и затем освобождает принадлежащий ему ресурс, когда указатель выходит из области видимости.
shared_ptr
однако может делиться правами собственности на ресурс с другими экземплярами shared_ptr. Ресурс остается в живых до тех пор, пока один или несколько shared_ptr
экземпляры владеют им. Это техника автоматического управления памятью (форма сборки мусора), известная как подсчет ссылок. По сути, он обеспечивает тот же эффект, что и более продвинутые алгоритмы сборки мусора, за исключением того, что в отличие от других методов сбора мусора он не обрабатывает циклические ссылки.
Что касается использования std::vector
против boost::scoped_array
, да уж - scoped_array
на самом деле не предлагает много преимуществ. Тем не мение, boost::shared_array
предлагает семантику подсчета ссылок, как shared_ptr
,