Умные указатели: или кто владеет тобой, детка?
C++ - все о владении памятью
Ака "Собственность Семантика"
Ответственность за освобождение этой памяти лежит на владельце куска динамически выделяемой памяти. Таким образом, вопрос действительно становится тем, кому принадлежит память.
В C++ владение задокументировано по типу, в который обернут указатель RAW, поэтому в хорошей (IMO) программе на C++ очень редко [RARE not NEVER] можно видеть, как RAW-указатели передаются (так как RAW-указатели не имеют предполагаемого владения, поэтому мы не можем скажите, кому принадлежит память, и, таким образом, без тщательного прочтения документации вы не можете сказать, кто несет ответственность за владение).
И наоборот, редко можно увидеть указатели RAW, хранящиеся в классе, каждый указатель RAW хранится в своей собственной оболочке указателя SMART. (NB. Если у вас нет объекта, вам не следует его хранить, потому что вы не можете знать, когда он выйдет из области видимости и будет уничтожен.)
Итак, вопрос:
- С какими типами владения Semantic люди сталкиваются?
- Какие стандартные классы используются для реализации этой семантики?
- Какие ситуации вы считаете их полезными?
Позволяет сохранить 1 тип семантической собственности на ответ, чтобы за них можно было голосовать за и против индивидуально
Резюме:
Концептуально умные указатели просты, а наивные реализации просты. Я видел много попыток реализации, но они неизменно ломаются, что не очевидно для случайного использования и примеров. Поэтому я рекомендую всегда использовать хорошо проверенные "умные указатели" из библиотеки, а не использовать собственные. std:: auto_ptr или один из улучшенных интеллектуальных указателей, кажется, покрывают все мои потребности.
станд:: auto_ptr:
Один человек владеет объектом.
Но передача права собственности разрешена.
Использование:
======
Это позволяет вам определять интерфейсы, которые показывают явную передачу права собственности.
повышение:: scoped_ptr
Один человек владеет объектом.
Передача права собственности не допускается.
Использование:
======
Используется для демонстрации явного владения.
Объект будет уничтожен деструктором или при явном сбросе.
boost:: shared_ptr (std:: tr1:: shared_ptr)
Многократное владение.
Это простой указатель с подсчетом ссылок. Когда счетчик ссылок достигает нуля, объект уничтожается.
Использование:
======
Когда у объекта может быть несколько Owers со временем жизни, которое не может быть определено во время компиляции.
повышение::weak_ptr
Используется с shared_ptr
В ситуациях, когда может случиться цикл указателей.
Использование:
======
Используется для остановки циклов от сохранения объектов, когда только цикл поддерживает общий счет.
11 ответов
Для меня эти 3 вида покрывают большинство моих потребностей:
shared_ptr
- подсчет ссылок, освобождение, когда счетчик достигает нуля
weak_ptr
- то же, что и выше, но это "раб" для shared_ptr
не может освободить
auto_ptr
- когда создание и освобождение происходят внутри одной и той же функции или когда объект должен считаться единственным владельцем когда-либо. Когда вы присваиваете один указатель другому, второй "крадет" объект у первого.
У меня есть своя собственная реализация для них, но они также доступны в Boost
,
Я все еще передаю объекты по ссылке (const
по возможности), в этом случае вызываемый метод должен предполагать, что объект жив только во время вызова.
Есть другой тип указателя, который я использую и называю hub_ptr. Это когда у вас есть объект, который должен быть доступен из вложенных в него объектов (обычно в виде виртуального базового класса). Это может быть решено путем передачи weak_ptr
для них, но это не имеет shared_ptr
к себе. Поскольку он знает, что эти объекты не будут жить дольше его, он передает им hub_ptr (это просто оболочка шаблона для обычного указателя).
Простая модель C++
В большинстве модулей, которые я видел, по умолчанию предполагалось, что указатели получения не получают владения. Фактически, функции / методы, отказывающиеся от владения указателем, были очень редкими и явно выражали этот факт в своей документации.
Эта модель предполагает, что пользователь является владельцем только того, что он / она явно выделяет. Все остальное автоматически удаляется (при выходе из области видимости или через RAII). Это C-подобная модель, расширенная тем фактом, что большинство указателей принадлежат объектам, которые освобождают их автоматически или при необходимости (в основном при уничтожении указанных объектов), и что продолжительность жизни объектов предсказуема (RAII - ваш друг, снова).
В этой модели необработанные указатели свободно циркулируют и в основном не опасны (но если разработчик достаточно умен, он / она будет использовать ссылки вместо этого всякий раз, когда это возможно).
- сырые указатели
- станд::auto_ptr
- повышение::scoped_ptr
Смарт Остроконечный C++ Модель
В коде, полном умных указателей, пользователь может надеяться игнорировать время жизни объектов. Владелец никогда не является пользовательским кодом: это сам умный указатель (снова RAII). Проблема в том, что циклические ссылки, смешанные с умными указателями с подсчетом ссылок, могут быть смертельными, поэтому вам приходится иметь дело как с общими, так и со слабыми указателями. Таким образом, у вас все еще есть право собственности (слабый указатель вполне может указывать на ничто, даже если его преимущество перед необработанным указателем состоит в том, что он может сказать вам об этом).
- повышение::shared_ptr
- повышение::weak_ptr
Заключение
Независимо от моделей, которые я описываю, за исключением исключения, получение указателя не получает его владельца, и все равно очень важно знать, кто кому принадлежит. Даже для кода C++ интенсивно используются ссылки и / или умные указатели.
Не имеет общей собственности. Если вы это сделаете, убедитесь, что это только с кодом, который вы не контролируете.
Это решает 100% проблем, поскольку заставляет вас понимать, как все взаимодействует.
- Совместная собственность
- повышение::shared_ptr
Когда ресурс используется несколькими объектами. Повышение shared_ptr использует подсчет ссылок, чтобы убедиться, что ресурс распределяется, когда все заканчивают.
Из boost есть также библиотека контейнеров указателей. Они немного эффективнее и проще в использовании, чем стандартный контейнер интеллектуальных указателей, если вы будете использовать объекты только в контексте их контейнера.
В Windows есть COM-указатели (IUnknown, IDispatch и друзья) и различные умные указатели для их обработки (например, CComPtr ATL и умные указатели, автоматически сгенерированные оператором import в Visual Studio на основе класса _com_ptr).
Существует еще одна часто используемая форма единственного передаваемого владельца, и она предпочтительнее auto_ptr
потому что это позволяет избежать проблем, вызванных auto_ptr
Безумная порча семантики присваивания.
Я не говорю ни о ком другом swap
, Любой тип с подходящим swap
Функция может восприниматься как интеллектуальная ссылка на некоторый контент, которым она владеет до тех пор, пока владение не будет передано другому экземпляру того же типа путем их замены. Каждый экземпляр сохраняет свою идентичность, но привязывается к новому контенту. Это как надежно повторяемая ссылка.
(Это умная ссылка, а не умный указатель, потому что вам не нужно явно разыменовывать его, чтобы получить доступ к контенту.)
Это означает, что auto_ptr становится менее необходимым - это нужно только для заполнения пробелов, где типы не имеют хорошего swap
функция. Но все стандартные контейнеры делают.
yasper::ptr - это легковесная альтернатива boost::shared_ptr. Это хорошо работает в моем (пока) небольшом проекте.
На веб-странице http://yasper.sourceforge.net/ это описывается следующим образом:
Зачем писать еще один умный указатель C++? Уже существует несколько высококачественных реализаций интеллектуальных указателей для C++, в частности, пантеон Boost и SmartPtr от Loki. Для хорошего сравнения реализаций интеллектуальных указателей и того, когда их использование уместно, прочитайте "Новые указатели C++: Smart(er)" Херба Саттера. В отличие от расширенных возможностей других библиотек, Yasper является узконаправленным указателем подсчета ссылок. Это близко соответствует политикам Boost's shared_ptr и Loki RefCounting /AllowConversion. Yasper позволяет программистам на C++ забыть об управлении памятью, не представляя больших зависимостей Boost и не узнавая о сложных шаблонах политики Loki. философия
* small (contained in single header) * simple (nothing fancy in the code, easy to understand) * maximum compatibility (drop in replacement for dumb pointers)
Последний пункт может быть опасным, так как yasper допускает рискованные (но полезные) действия (такие как присвоение необработанным указателям и ручное освобождение), запрещенные другими реализациями. Будьте осторожны, используйте эти функции, только если вы знаете, что делаете!
- Один владелец
- повышение::scoped_ptr
Когда вам нужно динамически распределять память, но вы хотите быть уверены, что она освобождается в каждой точке выхода блока.
Я считаю это полезным, поскольку его можно легко переустановить и освободить, не беспокоясь о утечке.
Я не думаю, что когда-либо был в состоянии разделить собственность в моем дизайне. На самом деле, с одной стороны, единственно верный случай, о котором я могу подумать, это модель Flyweight.
- Один владелец: ака удалить при копировании
- станд::auto_ptr
Когда создатель объекта хочет явно передать право собственности кому-либо еще. Это также способ документирования в коде, который я вам даю, и я больше не отслеживаю его, поэтому обязательно удалите его, когда закончите.