为什么共享指针分配会"交换"?

Why shared pointer assignment does 'swap'?

本文关键字:交换 分配 共享 指针 为什么      更新时间:2023-10-16

正如我所理解的,当分配共享ptr时,其行为应该像:

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

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

助推只是

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

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

a) 如果(--this->count==0){释放this->count和this->obj}

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

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

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

所以你的理解是不完整/不正确的,但如果你仔细阅读Boost代码,你会发现它做得完全正确。它增加了r对象上的引用计数,交换了所有必要的值,然后减少了*this原始值的引用计数。

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

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

减少引用计数(并在需要时释放任何内容)的步骤已经由析构函数完成,所以它使用它。

这是极好的代码重用。另一种选择是重复复制构造函数、交换成员和析构函数中的所有相同代码,这将是冗余的并且容易出错。

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

要分配ref count对象,首先(a)增加右手边的ref count,然后(b)减少左手边的ref计数,以及(c)将rhs的状态存储在lhs上。

只有在可能发生异常的情况下,(b)和(c)的顺序才重要,但如果存在自分配(或等效),则(a)必须发生在(b)之前。

复制交换习惯用法是这样做的:

  • (1) 将rhs复制到临时
  • (2) 用临时(包含rhs状态的副本)交换lhs状态
  • (3) 销毁临时这在(1)中做了(a),然后在(2)中做(c),然后是在(3)中做的(b)。它以一种特别安全的异常方式进行:代码处于"奇怪"状态的唯一时间是在交换中,并且交换很容易进行异常验证

拷贝交换可以作为一种通用的易于编写的分配,但它不重用lhs的内部资源,这可能会降低成本。在这种情况下,这并不重要,因为lhs的内部资源是指向共享状态的指针。

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