Подтвердить что ты не робот

Shared_ptr <> относится к weak_ptr <> как unique_ptr <> для... чего?

В С++ 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< Trebuchet > 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< Victim > createVictim( World& world )
{
    return make_shared< Victim >( world.trebuchet() );
}

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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() );
}

Ответ 3

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

Ответ 4

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

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

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

Тема 1:

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

Резьба2:

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

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

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

Ответ 5

boost::optional<Trebuchet&>

Как указал Билли Онел в своем ответе, вы скорее всего хотите передать 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 < T & gt; vs T *

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

Ответ 6

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

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

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

Ответ 7

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

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

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

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