shared_ptr относится к weak_ptr как unique_ptr для … чего?

В C ++ 11 вы можете использовать shared_ptr чтобы установить отношение собственности с объектом или переменной и weak_ptr чтобы безопасно ссылаться на этот объект не принадлежащим ему образом.

Вы также можете использовать unique_ptr для установления отношения собственности с объектом или переменной. Но что, если другие, не связанные с владельцем объекты хотят также ссылаться на этот объект? weak_ptr в этом случае не поможет. Необработанные указатели полезны, но приносят различные недостатки (например, их можно автоматически инициализировать до nullptr, но это достигается с помощью методов, которые не соответствуют типам std::*_ptr ).

Что эквивалентно weak_ptr для не владеющих ссылками на объекты, принадлежащие unique_ptr ?

Вот пояснительный пример, который напоминает что-то в игре, над которой я работаю.

 class World { public: Trebuchet* trebuchet() const { return m_trebuchet.get(); } private: std::unique_ptr m_trebuchet; }; class Victim { public: Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {} ~Victim() { delete m_trebuchet; // Duh. Oops. Dumb error. Nice if the compiler helped prevent this. } private: Trebuchet* m_trebuchet; // Non-owning. }; shared_ptr createVictim( World& world ) { return make_shared( world.trebuchet() ); } К class World { public: Trebuchet* trebuchet() const { return m_trebuchet.get(); } private: std::unique_ptr m_trebuchet; }; class Victim { public: Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {} ~Victim() { delete m_trebuchet; // Duh. Oops. Dumb error. Nice if the compiler helped prevent this. } private: Trebuchet* m_trebuchet; // Non-owning. }; shared_ptr createVictim( World& world ) { return make_shared( world.trebuchet() ); } 

Здесь мы используем необработанный указатель для поддержания отношений, отличных от владельца, с объектом, принадлежащим unique_ptr другом месте. Но сырье самое лучшее, что мы можем сделать?

Надежда – это тип указателя, который:

  • Похоже на другие современные типы указателей. Например std::raw_ptr .
  • Заменяет исходные указатели так, чтобы база кода, которая использует современные типы указателей, может найти все указатели с помощью поиска _ptr< (грубо).
  • Автоматически инициализирует значение nullptr.

Таким образом:

 int* p; // Unknown value. std::raw_ptr p; // null. 

Этот тип уже существует в C ++ сейчас, предлагается ли он на будущее или является другой реализацией, широко доступной, например, Boost?

«Уведомлять» поведение shared_ptr требует ссылки, подсчитывающей контрольный блок ссылок. shared_ptr блоке (-ах) контроля счетчика shared_ptr используются отдельные подсчеты ссылок. weak_ptr экземпляров weak_ptr ссылки на этот блок, а сами weak_ptr не позволяют delete контрольный блок ссылок. Указанный объект имеет свой деструктор, называемый, когда сильное количество отсчитывается до нуля (что может или не может привести к delete памяти, в которой был сохранен этот объект), а блок управления delete изредка только при слабом подсчете ссылок до нуля.

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

Если вам нужна общая неактивная ссылка и не требуется уведомление, используйте простые указатели или простые ссылки на элемент в unique_ptr .


РЕДАКТИРОВАТЬ:

В случае вашего примера, похоже, что Victim должна попросить Trebuchet& а не Trebuchet* . Тогда ясно, кто владеет объектом, о котором идет речь.

 class World { public: Trebuchet& trebuchet() const { return *m_trebuchet.get(); } private: std::unique_ptr< Trebuchet > m_trebuchet; }; class Victim { public: Victim( Trebuchet& theTrebuchet ) : m_trebuchet( theTrebuchet ) {} ~Victim() { delete m_trebuchet; // Compiler error. :) } private: Trebuchet& m_trebuchet; // Non-owning. }; shared_ptr< Victim > createVictim( World& world ) { return make_shared< Victim >( world.trebuchet() ); } 

Существует настоятельная потребность в стандартном типе указателя, который будет действовать как не владеющий, недорогой и хорошо управляемый контрапункт для std::unique_ptr<> . Такой указатель не был стандартизирован, но стандарт был предложен и обсуждается комитетом по стандартам С ++. «Самый глупый смарт-указатель мира», так же как std::exempt_ptr<> будет иметь общую семантику других современных classов указателей на C ++, но не будет нести ответственности ни за то, что он владеет объектом, указанным для объекта (как shared_ptr и unique_ptr do), так и для правильного ответа к удалению этого объекта (как weak_ptr делает weak_ptr ).

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

unique_ptr аналог unique_ptr является простым указателем C. Что другое – указатель C не знает, доступны ли заостренные данные. weak_ptr другой стороны, weak_ptr . Но невозможно заменить raw указатель указателем, зная о действительности данных без дополнительных накладных расходов (а у weak_ptr есть эти накладные расходы). Это означает, что указатель C-стиля является лучшим с точки зрения скорости, которую вы можете получить как не имеющий аналогов для unique_ptr .

Хотя вы не можете бесплатно получить «слабый» указатель на объект, принадлежащий уникальным объектам, эта концепция полезна и используется в нескольких системах. См. Chromium’s WeakPtr и QPointer QT для реализаций.

Chromium’s WeakPtr реализуется навязчиво, сохраняя shared_ptr внутри объекта с низкой ссылочной информацией и отмечая его недействительным, когда объект уничтожается. Затем WeakPtrs ссылаются на ControlBlock и проверяют, действительно ли это, прежде чем передать свой необработанный указатель. Я предполагаю, что QT QPointer реализуется аналогичным образом. Поскольку собственность не разделяется, исходный объект уничтожается детерминированным образом.

Однако это означает, что разыменование WeakUniquePtr не является streamобезопасным:

Тема 1:

 unique_ptr obj(new MyObject); thread2.send(obj->AsWeakPtr()); ... obj.reset(); // A 

Резьба2:

 void receive(WeakUniquePtr weak_obj) { if (MyObject* obj = weak_obj.get()) { // B obj->use(); } } 

Если строка A запускается одновременно с линией B , stream 2 завершается с помощью висячего указателя. std::weak_ptr предотвратит эту проблему путем атомарного использования общедоступной ссылки на объект, прежде чем позволить использовать stream 2 , но это нарушает предположение выше, что объект принадлежит однозначно. Это означает, что любое использование WeakUniquePtr необходимо синхронизировать с разрушением реального объекта, а самый простой способ сделать это – потребовать, чтобы они выполнялись в цикле сообщений в одном streamе. (Обратите внимание, что по-прежнему полностью безопасно копировать WeakUniquePtr вперед и назад по streamам, прежде чем использовать его.)

Можно было бы предположить использование пользовательского удаления в std::unique_ptr для реализации этого с использованием стандартных типов библиотек, но это оставлено как упражнение для читателя.

 boost::optional 

Как указал Билли Онел в своем ответе, вы, скорее всего, захотите передать Trebuchet& вместо указателя. Проблема со ссылкой состоит в том, что вы не можете передать nullptr , boost::optional предоставляет способ иметь эквивалент nullptr. Более подробная информация о boost :: optional приведена здесь: http://www.boost.org/doc/libs/1_54_0/libs/optional/doc/html/boost_optional/detailed_semantics.html

См. Также этот вопрос: boost :: optional vs T *

Примечание: std::optional находится на пути, чтобы сделать его в C ++ 14, но std::optional – это отдельное предложение, которое не находится в текущем проекте C ++ 14. Более подробная информация здесь: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3672.html

В новом мире C ++ с shared_ptr, weak_ptr и unique_ptr вам не следует хранить долговечные ссылки на объекты, например, ваш требуше, используя исходные указатели или ссылки. Вместо этого мир должен иметь shared_ptr для trebuchet, и жертве следует хранить либо shared_ptr, либо weak_ptr, в зависимости от того, должен ли trebuchet стоять рядом с жертвой, если мир уйдет. Использование функции weak_ptr позволяет вам определить, действительно ли указатель действителен (т. Е. Мир все еще существует), нет способа сделать это с помощью необработанного указателя или ссылки.

Когда вы используете unique_ptr, вы заявляете, что только экземпляр World будет владеть trebuchet. Клиенты classа World могут использовать trebuchet объекта World, вызывая метод get, но не должны удержаться на ссылке или указателе, возвращаемом методом, когда они будут выполнены с его помощью. Вместо этого они должны «заимствовать» trebuchet каждый раз, когда захотят использовать его, вызвав метод «get».

Выше сказано, что могут быть случаи, когда вы хотите сохранить ссылку или необработанный указатель для будущего использования, чтобы избежать накладных расходов shared_ptr. Но этих экземпляров мало и далеко друг от друга, и вам нужно быть абсолютно уверенным, что вы не будете использовать указатель или ссылку после того, как объект World, которому принадлежит trebuchet, ушел.

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

Если вы хотите удержать указатель, вы делитесь им (и должны использовать shared_ptr ). unique_ptr управляет одной копией указателя. Вы используете необработанные указатели (или ссылки), чтобы ссылаться на функции вызова, связанные с этим объектом.

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

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

  • Копировать конструктор для classа с unique_ptr
  • Различия между unique_ptr и shared_ptr
  • Вызов конструктора initializer_list через make_unique / make_shared
  • «Downcasting» unique_ptr to unique_ptr
  • unique_ptr и STSE_OF (X509) OpenSSL *
  • Как захватить unique_ptr в выражение lambda?
  • unique_ptr boost эквивалент?
  • Как использовать unique_ptr для pimpl?
  • Форвардная декларация с уникальным_ptr?
  • Давайте будем гением компьютера.