Умные указатели: кому принадлежит объект?

C++ - это владение памятью - он же семантика владения.

Ответственность за освобождение этой памяти лежит на владельце куска динамически выделяемой памяти. Таким образом, возникает вопрос: кому принадлежит память?

В C++ владение задокументировано по типу, в который обернут необработанный указатель, поэтому в хорошей (IMO) C++ программе очень редко (редко, не раз) можно видеть, как необработанные указатели передаются (поскольку необработанные указатели не имеют никаких выводов). Таким образом, мы не можем сказать, кому принадлежит память, и, следовательно, без внимательного прочтения документации вы не сможете сказать, кто несет ответственность за владение.)

И наоборот, редко можно увидеть необработанные указатели, хранящиеся в классе, причем каждый необработанный указатель хранится в его собственной оболочке интеллектуальных указателей. (N.B.: Если у вас нет объекта, вам не следует его хранить, потому что вы не можете знать, когда он выйдет из области видимости и будет уничтожен.)

Итак, вопрос:

  • С какими семантическими формами владения сталкиваются люди?
  • Какие стандартные классы используются для реализации этой семантики?
  • В каких ситуациях вы находите их полезными?

Позволяет сохранить 1 тип семантической собственности на ответ, чтобы за них можно было голосовать "за" и "против" индивидуально.

Резюме:

Концептуально умные указатели просты, а наивная реализация проста. Я видел много попыток реализации, но они неизменно ломаются, что не очевидно для случайного использования и примеров. Поэтому я рекомендую всегда использовать хорошо проверенные умные указатели из библиотеки, а не использовать собственные. std::auto_ptr или один из умных указателей Boost, кажется, покрывает все мои потребности.

std::auto_ptr<T>:

Один человек владеет объектом. Передача права собственности разрешена.

Использование: Это позволяет вам определять интерфейсы, которые показывают явную передачу прав собственности.

boost::scoped_ptr<T>

Один человек владеет объектом. Передача права собственности не допускается.

Использование: используется, чтобы показать явное владение. Объект будет уничтожен деструктором или при явном сбросе.

boost::shared_ptr<T> (std::tr1::shared_ptr<T>)

Многократное владение. Это простой указатель с подсчетом ссылок. Когда счетчик ссылок достигает нуля, объект уничтожается.

Использование: когда объект может иметь несколько владельцев со сроком службы, который не может быть определен во время компиляции.

boost::weak_ptr<T>:

Используется с shared_ptr<T> в ситуациях, когда может произойти цикл указателей.

Использование: Используется для остановки циклов от сохранения объектов, когда только цикл поддерживает общий счет.

Ответы

Ответ 1

Для меня эти 3 вида покрывают большинство моих потребностей:

shared_ptr - отсчет отсчета, освобождение, когда счетчик достигает нуля

weak_ptr - то же, что и выше, но это "подчиненный" для shared_ptr, не может освободить

auto_ptr - когда создание и освобождение происходят внутри одной и той же функции или когда объект должен считаться только владельцем. Когда вы назначаете один указатель на другой, второй "крадет" объект из первого.

У меня есть своя реализация для них, но они также доступны в Boost.

Я все еще передаю объекты по ссылке (const, когда это возможно), в этом случае вызываемый метод должен предположить, что объект жив только во время вызова.

Там другой вид указателя, который я использую, я называю hub_ptr. Это когда у вас есть объект, который должен быть доступен из вложенных в него объектов (обычно это виртуальный базовый класс). Это можно решить, передав им weak_ptr, но он не имеет shared_ptr для себя. Поскольку он знает, что эти объекты не будут жить дольше, чем он, он передает им hub_ptr (это просто оболочка шаблона для обычного указателя).

Ответ 2

Простая модель С++

В большинстве модулей я видел, что по умолчанию предполагалось, что получающие указатели не получают право собственности. Фактически, функции/методы, отказавшиеся от владения указателем, были очень редкими и явно выражали этот факт в своей документации.

Эта модель предполагает, что пользователь является владельцем только того, что он/она явно выделяет. Все остальное автоматически удаляется (при выходе из области или через RAII). Это C-образная модель, расширенная тем фактом, что большинство указателей принадлежат объектам, которые будут автоматически освобождать их или когда это необходимо (при основном уничтожении объектов) и что продолжительность жизни объектов предсказуема (RAII - ваш друг, снова).

В этой модели необработанные указатели свободно циркулируют и в основном не опасны (но если разработчик достаточно умен, он/она будет использовать ссылки, когда это возможно).

  • raw указатели
  • станд:: auto_ptr
  • подталкивание:: scoped_ptr

Smart Pointed С++ Model

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

  • повышение:: shared_ptr
  • подталкивание:: weak_ptr

Заключение

Независимо от моделей, которые я описываю, , если исключение, получающее указатель, не получает его права собственности и , по-прежнему очень важно знать, кому принадлежит. Даже для С++-кода, сильно использующего ссылки и/или интеллектуальные указатели.

Ответ 3

У вас нет совместного доступа. Если вы это сделаете, убедитесь, что это только с кодом, который вы не контролируете.

Это решает 100% проблем, поскольку это заставляет вас понять, как все взаимодействует.

Ответ 4

  • Общая собственность
  • повышение:: shared_ptr

Когда ресурс разделяется между несколькими объектами. В boost shared_ptr используется подсчет ссылок, чтобы гарантировать, что ресурс будет выделен, когда все будут финишированы.

Ответ 5

std::tr1::shared_ptr<Blah> нередко ваш лучший выбор.

Ответ 6

Из boost также существует контейнер указателя. Они немного эффективнее и проще в использовании, чем стандартный контейнер интеллектуальных указателей, если вы будете использовать объекты только в контексте их контейнера.

В Windows есть указатели COM (IUnknown, IDispatch и друзья) и различные интеллектуальные указатели для их обработки (например, ATL CComPtr и интеллектуальные указатели, автоматически генерируемые оператором "import" в Visual Studio на основе класса _ com_ptr).

Ответ 7

  • Один владелец
  • подталкивание:: scoped_ptr

Когда вам нужно динамически выделять память, но хотите быть уверенным, что она будет освобождена в каждой точке выхода блока.

Я нахожу это полезным, так как его можно легко переустановить и освободить, не беспокоясь о утечке

Ответ 8

Я не думаю, что когда-либо был в состоянии иметь совместное владение в моем дизайне. На самом деле, с моей точки зрения, единственным действительным случаем, о котором я могу думать, является шаблон Flyweight.

Ответ 9

yasper:: ptr - это облегченная, boost:: shared_ptr альтернатива. Он хорошо работает в моем (на данный момент) небольшом проекте.

На веб-странице http://yasper.sourceforge.net/ он описывается следующим образом:

Зачем писать другой смарт-указатель на С++? Там уже существует несколько высоких реализация интеллектуальных указателей качества для С++, наиболее заметно Boost указательный пантеон и Loki SmartPtr. Для хорошего сравнения умного указателя и когда их использование подходящий, пожалуйста, прочитайте статью Herb Sutter's Новые указатели С++: Smart (er). В контрастирует с экспансивными функциями других библиотек, Яспер - это узко ориентированный подсчет ссылок указатель. Он тесно связан с Boost shared_ptr и Loki's RefCounted/AllowConversion. Yasper позволяет программистам на С++ забыть об управлении памятью без введение Boost large зависимостей или Локи усложнил шаблоны политики. Философия

* small (contained in single header)
* simple (nothing fancy in the code, easy to understand)
* maximum compatibility (drop in replacement for dumb pointers)

Последний пункт может быть опасным, поскольку yasper позволяет рисковать (но полезно) действия (например, присвоение сырья указатели и ручной выпуск) запрещенных другими реализациями. Будьте осторожны, используйте только те функции, если вы знаете, что делаете!

Ответ 10

Существует еще одна часто используемая форма владельца с одной передачей, и она предпочтительнее auto_ptr, поскольку она позволяет избежать проблем, вызванных auto_ptr безумным искажением семантики присваивания.

Я говорю только о swap. Любой тип с подходящей функцией swap может рассматриваться как интеллектуальная ссылка на некоторый контент, который он владеет до тех пор, пока собственность не будет передана другому экземпляру того же типа, путем их замены. Каждый экземпляр сохраняет свою личность, но привязывается к новому контенту. Это похоже на безопасно восстановимую ссылку.

(Это умная ссылка, а не умный указатель, потому что вам не нужно явно разыгрывать ее, чтобы попасть в контент.)

Это означает, что auto_ptr становится менее необходимым - ему нужно только заполнить пробелы, где типы не имеют хорошей функции swap. Но все контейнеры std.

Ответ 11

  • Один владелец: Aka delete on Copy
  • станд:: auto_ptr

Когда создатель объекта хочет явно передать собственность кому-то другому. Это также способ документирования кода, который я вам даю, и я больше не отслеживаю его, поэтому убедитесь, что вы удалите его, когда закончите.