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


68

我对C ++ 11最佳做法有疑问。清除shared_ptr时,应使用reset()不带参数的函数,还是应将设置shared_ptrnullptr?例如:

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

两种方法有什么真正的区别,还是有优点/缺点?


1
foo = {}也可以选择!
Luc Danton 2013年

那怎么-> std::shared_ptr<std::string> bar; foo = bar?通常,当我们将shared_ptr分配给另一个对象时,它指向的对象的引用计数会增加。在这种情况下会发生什么?
竞争对手dodo

Answers:


78

两种方法有什么真正的区别,还是有优点/缺点?

在第二种形式(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");

糟糕,感谢您的帮助,我在写字符串时没有注意。更新了问题以反映更改。
user1930581

1
我会出于@Andy Prowl所述的相同原因亲自使用nullptr。但是,请尝试编写代码,以使shared_ptr超出范围。
Trax

2
@MarkB:异常安全性和较少的分配:有关更详细的讨论,请参见例如this answer
安迪·普罗

5
好的,三年过去了。希望有人能读到...我注意到,您对标准的引用是针对的unique_ptr,也没有operator=(nullptr_t)针对的方法shared_ptr。是正确的吗,如果我使用的话,nullptr将被强制转换为unique_ptr然后强制转换为?shared_ptrfoo = nullptr
mdr

1
我很困惑,一个关于一个不同的类的答案,甚至从来没有承认过,它是如此被强烈推荐。正如@mdr所说,r0ng显示(尽管没有足够的细节或对优化的关注),shared_ptr没有operator=(nullptr_t),因此分配nullptr给它需要转换。我们可能会证明,在优化的构建中这并不重要,但要说“这两种选择是绝对等价的,因为第二种形式(foo = nullptr)是根据第一种形式定义的”,这似乎是完全错误的
underscore_d

15

我希望这样reset()做,因为它表明了意图。但是,请尝试编写代码,以使您无需显式清除a shared_ptr<>,即,确保shared_ptr<>在其他情况下清除a时都超出范围。


您能否说出反对清除指针的建议背后的原因?是否有性能下降或“只是”没有空指针的情况?
罗伯特·

3
@RobertF。不,不会影响性能。只是,需要经常清除指针可能表明存在设计缺陷。
Walter

3

如果您使用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创建一个共享指针,将新创建的对象分配给该变量,并将destructor调用到destory字符串。

由于我不知道如何检查函数reset()中发生了什么。看不清楚哪个更快。


8
如果同时使用-O2进行编译,则会发现发行版本没有什么不同。
RandomGuy

-1

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


3
仅陈述意见并不能回答问题,该问题至关重要地包括推理请求:“这两种方法是否有真正的区别,还是有优点/缺点?
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.