C++11 清除shared_ptr时,我应该使用reset还是设置为nullptr?

Posted

技术标签:

【中文标题】C++11 清除shared_ptr时,我应该使用reset还是设置为nullptr?【英文标题】:C++11 When clearing shared_ptr, should I use reset or set to nullptr? 【发布时间】:2013-04-15 14:48:33 【问题描述】:

我有一个关于 C++11 最佳实践的问题。清除shared_ptr时,我应该使用不带参数的reset()函数,还是应该将shared_ptr设置为nullptr?例如:

std::shared_ptr<std::string> foo(new std::string("foo"));
foo.reset();
foo = nullptr;

有什么真正的区别,或者这两种方法都有优点/缺点吗?

【问题讨论】:

foo = 也是一种选择! 这个怎么样 -> std::shared_ptr&lt;std::string&gt; bar; foo = bar?通常,当我们将 shared_ptr 分配给另一个时,它指向的对象的引用计数会增加。在这种情况下会发生什么? 【参考方案1】:

我更喜欢reset(),因为它表明了意图。但是,请尝试编写您的代码,以便您不需要显式清除 shared_ptr&lt;&gt;,即,确保 shared_ptr&lt;&gt; 在您原本要清除它的情况下超出范围。

【讨论】:

您能说出反对清除指针的建议背后的原因吗?是否有任何性能损失或“只是”没有空指针? @RobertF。不,没有性能影响。只是,需要频繁清除指针可能表明存在设计缺陷。【参考方案2】:

如果您使用https://godbolt.org/ 进行检查,它们会有所不同 通过使用 gcc(7.2)foo.reset(); 生成汇编代码

  lea rax, [rbp-32]
  mov rdi, rax
  call std::__shared_ptr<int, (__gnu_cxx::_Lock_policy)2>::reset()

然而, foo = nullptr; 生成

  lea rax, [rbp-16]
  mov esi, 0
  mov rdi, rax
  call std::shared_ptr<int>::shared_ptr(decltype(nullptr))
  lea rdx, [rbp-16]
  lea rax, [rbp-32]
  mov rsi, rdx
  mov rdi, rax
  call std::shared_ptr<int>::operator=(std::shared_ptr<int>&&)
  lea rax, [rbp-16]
  mov rdi, rax
  call std::shared_ptr<int>::~shared_ptr()

它使用nullptr创建一个共享指针,将新创建的对象分配给变量并调用析构函数来销毁字符串。

因为我不知道如何检查函数 reset() 中发生了什么。看不出哪个更快。

【讨论】:

如果您使用-O2 编译两者,您会发现发布版本没有区别。【参考方案3】:

有什么真正的区别,或者这两种方法都有优点/缺点吗?

在第二种形式 (foo = nullptr) 是根据第一种形式定义的意义上,这两种选择是绝对等价的。根据 C++11 标准的第 20.7.1.2.3/8-10 段:

 unique_ptr& operator=(nullptr_t) noexcept;

8 效果reset()

9 后置条件get() == nullptr

10 返回*this

因此,只需选择其意图对您来说最清晰的一个。就个人而言,我更喜欢:

foo = nullptr;

因为它使我们希望指针为空变得更加明显。但是,作为一般建议,请尽量减少需要显式重置智能指针的情况。


另外,而不是使用new

std::shared_ptr<std::string> foo(new std::string("foo"));

尽可能考虑使用std::make_shared()

auto foo = std::make_shared<std::string>("foo");

【讨论】:

糟糕,感谢您的发现,我在写出字符串时没有注意。更新了问题以反映变化。 出于@Andy Prowl 所述的相同原因,我个人会使用 nullptr。但是尝试编写代码,使 shared_ptr 超出范围。 @MarkB:异常安全和少分配:有关更详细的讨论,请参阅例如this answer。 好吧,三年过​​去了。希望有人会读到这篇文章...我注意到,您对标准的引用是针对 unique_ptr 的,而且对于 shared_ptr 也没有 operator=(nullptr_t) 方法。是否正确,如果我使用 foo = nullptrnullptr 将被转换为 unique_ptr,然后转换为 shared_ptr 我很困惑,一个谈论一个不同的类甚至没有承认它的答案是如此高度赞成。正如@mdr 和r0ng showed 所说(尽管没有足够的细节或注意优化),shared_ptr 没有operator=(nullptr_t),因此将nullptr 分配给它需要转换。我们可能可以证明在优化构建中并不重要,但要说“这两种选择是绝对等价的,因为第二种形式 (foo = nullptr) 是根据第一种形式定义的” 似乎完全错误【参考方案4】:

通常,智能指针可以自行处理。但如果您需要解决方案,我认为reset() 是您的最佳选择。

【讨论】:

仅仅陈述一个观点并不能回答这个问题,关键是要求推理:“有什么真正的区别,或者这两种方法有优点/缺点吗? "

以上是关于C++11 清除shared_ptr时,我应该使用reset还是设置为nullptr?的主要内容,如果未能解决你的问题,请参考以下文章

[C++11]shared_ptr共享智能指针的初始化与使用

[C++11]shared_ptr使用的注意事项(内存被重复析构,内存泄漏问题)

c ++ shared_ptr和memcpy错误[重复]

C++11 shared_ptr(智能指针)详解

shared_ptr智能指针模板类的简单实现(c++11)

c++三种使用shared_ptr的方式