当您使用shared_ptr
的复制赋值操作符时,从概念上讲,赋值左侧的shared_ptr
需要减去它当前拥有的对象的引用计数,然后增加赋值右侧对象的引用计数。(当然,假设两个指针都是非空的。)
所以实现可能看起来像下面的伪代码:
shared_ptr& operator = (const shared_ptr& rhs)
{
decrement_reference_count(this->m_ptr);
this->m_ptr = rhs.m_ptr;
increment_reference_count(this->m_ptr);
return *this;
}
但请注意,这里我们在之前减少this
的引用计数,我们增加rhs
的引用计数。我们也可以反过来做。我的问题是,标准中确实规定了顺序吗?
为什么会产生差异:如果this
的引用计数与lhs
的引用计数之间存在某种依赖关系,则会产生很大的差异。例如,假设两者都是链表结构的一部分,其中每个链接节点中的next
指针都是shared_ptr
。因此,递减结构中任何节点的引用计数都可能触发析构函数,而析构函数随后会引发链式反应,并递减引用计数(可能还会破坏)链中的每个其他节点。
lhs
的引用计数受到this
的引用计数的影响,那么首先减少this
还是首先增加lhs
会有很大的区别。如果在对this
进行递减之前先对lhs
进行递增,则可以确保在对this
进行递减时lhs
不会被破坏。
但是标准在这里真的指定了顺序吗?据我所知,标准唯一说的是复制赋值操作符等价于表达式:
shared_ptr(lhs).swap(*this)
但是我不能真正理解这种等价对于引用计数递减/递增的顺序可能具有的含义(如果有的话)。
那么标准在这里指定顺序了吗?或者这是实现定义的行为?
The Standard说[20.7.2.2.3]
shared_ptr& operator=(const shared_ptr& r) noexcept;
的作用相当于
shared_ptr(r).swap(*this)
这意味着构造一个临时对象,它增加r
的引用计数,然后与*this
交换它的数据,然后销毁临时对象,这意味着减少以前属于*this
的引用计数。
如果rhs
是this
,则必须先增加引用计数器。否则,当引用计数器为1时,它可能会无意中销毁指针。它可以检查是否this == &rhs
,但如果引用计数器的增量在减量之前执行,则此检查是不必要的。
shared_ptr(lhs).swap(*this)
没有这个问题,因为它先创建一个副本,从而先增加引用计数器。