为什么共享指针赋值 'swap'?

Why shared pointer assignment does 'swap'?

据我所知,分配的共享指针应该表现得像:

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

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

boost的作用只是

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

那么交换背后的魔力是什么?为什么它有效?

到赋值运算符作用域结束时(结束 }),原始 sptr 对象的引用计数将减一,因为 r 不再指向它。因此,它在功能上正是您上面所描述的。

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

不,shared_ptr 保留两个计数,一个用于对象,一个用于包含引用计数的控制块。当第一个达到零时释放 this->obj,当第二个达到零时释放 this->count(和第二个计数)。

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

不,您缺少引用计数增量。此外,正如 Yakk 的回答所指出的那样,您必须检查自赋值或首先增加右侧,以避免在自赋值时错误地破坏对象。

所以您的理解是 incomplete/incorrect,但是如果您按照 Boost 代码进行操作,您会发现它做的事情完全正确。它增加 r 对象的引用计数,交换所有必要的值,然后减少 *this.

的原始值的引用计数

增加引用计数的步骤已经在复制构造函数中实现,因此它重新使用了它。

交换所有必要值的步骤已经在 swap 成员中实现,因此它使用它。

减少引用计数(并在需要时释放任何东西)的步骤已经由析构函数完成,因此它使用了那个。

这是很好的代码重用。另一种方法是重复在复制构造函数、交换成员和析构函数中找到的所有相同代码,这将是冗余且容易出错的。

这是复制交换习语。它并不总是有效,但在这种情况下是有效的。

要分配一个引用计数对象,您首先 (a) 增加右侧的引用计数,然后 (b) 减少左侧的引用计数,以及 (c) 存储 rhs 的状态在 lhs.

(b) 和 (c) 的顺序仅在可能发生异常时才重要,但 (a) 必须 在 (b) 之前发生,以防出现自赋值(或同等学历)。

copy swap idiom 这样做:

  • (1) 将 rhs 复制到临时文件。
  • (2) 用临时(包含 rhs 状态的副本)交换 lhs 状态
  • (3) 销毁临时 这会在 (1) 中执行 (a),然后在 (2) 中执行 (c),然后在 (3) 中执行 (b)。它以一种特别安全的异常方式来实现:代码处于 'strange' 状态的唯一时间是在交换中,并且交换很容易进行异常防护。

Copy swap可以作为一个通用的好写赋值,但是没有复用lhs的内部资源,这样比较节省成本。在这种情况下这并不重要,因为 lhs 内部资源是指向共享状态的指针。