Почему общий указатель присваивает "своп"?

Как я понимаю, когда назначенный общий ptr должен вести себя так:

а) if (--this->count == 0) { release this->count and this->obj }

б) this->count = r->count, this->obj = r->obj;

То, что делает повышение, это просто

shared_ptr & operator=( shared_ptr const & r ) BOOST_NOEXCEPT
{
    this_type(r).swap(*this);
    return *this;
}

Так что же такое магия подкачки и почему это работает?

3 ответа

Решение

а) if (--this->count == 0) {отпустить this-> count и this->obj }

Нет shared_ptr сохраняет два счета, один для объекта и один для блока управления, содержащего счетные ссылки. Вы отпускаете this->obj когда первый достигает нуля и выпускает this->count (и второй счет), когда второй достигает нуля.

б) this->count = r->count, this->obj = r->obj;

Нет, вам не хватает приращения счетчика ссылок. Кроме того, как указывает ответ Якка, вы должны либо проверять самопредставление, либо сначала увеличивать правую сторону, чтобы избежать неправильного уничтожения объекта при самопредставлении.

Таким образом, ваше понимание неполное / неправильное, но если вы выполните код Boost, вы увидите, что он делает все правильно. Это увеличивает количество ссылок на r объект, обменивает все необходимые значения, затем уменьшает счетчик ссылок на исходное значение *this,

Шаг увеличения количества ссылок уже реализован в конструкторе копирования, поэтому он использует его повторно.

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

Шаг уменьшения количества ссылок (и освобождения всего, что требуется) уже выполнен деструктором, поэтому он использует это.

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

Это идиома копирования-обмена. Это не всегда эффективно, но в этом случае это так.

Чтобы назначить объект подсчета ссылок, вы сначала (а) увеличиваете счетчик ссылок в правой части, затем (б) уменьшаете счетчик ссылок в левой части и (в) сохраняете состояние rhs на lhs.

Порядок (b) и (c) имеет значение только в том случае, если могут возникать исключения, но (a) должно произойти раньше, чем (b), если существует самоопределение (или эквивалент).

Вместо этого идиома копирования копии делает это:

  • (1) скопировать rhs во временный.
  • (2) поменять состояние lhs на временное (содержащее копию состояния rhs)
  • (3) уничтожить временно это делает (а) в (1), затем (с) в (2), затем (б) в (3). И делает это особенно безопасным образом: единственный раз, когда код находится в "странном" состоянии, находится внутри свопа, а своп легко сделать защищенным от исключений.

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

К концу области действия операторов присваивания (закрытие }), счетчик ссылок для исходного объекта sptr будет уменьшен на единицу, потому что r больше не указывает на это. Таким образом, это именно то, что вы описываете выше.

Другие вопросы по тегам