96

I have a question about C++11 best practices. When clearing a shared_ptr, should I use the reset() function with no parameter, or should I set the shared_ptr to nullptr? For example:

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

Is there any real difference, or are there advantages/disadvantages to either approach?

Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175
user1930581
  • 1,465
  • 1
  • 13
  • 23
  • 3
    `foo = {}` is also an option! – Luc Danton Apr 22 '13 at 18:46
  • 1
    What about this -> `std::shared_ptr bar; foo = bar`? Normally when we assign a shared_ptr to another, the ref count for the object it points to increases. What happens in this case? – rivaldo4t Jan 24 '20 at 23:47
  • @rivaldo4t If foo refers to another object at the point of assignment, then its shared ref count is automatically decremented -- before the assignment -- the same as explicitly calling reset or assigning nullptr before assigning. If that ref count is now zero, the object it previously referred to will be destroyed. The ref count shared with bar is then incremented as normal. – Peter Forrest Aug 23 '23 at 15:11

4 Answers4

103

Is there any real difference, or are there advantages/disadvantages to either approach?

The two alternatives are absolutely equivalent, in the sense that the second form (foo = nullptr) is defined in terms of the first one. Per Paragraph 20.7.1.2.3/8-10 of the C++11 Standard:

 unique_ptr& operator=(nullptr_t) noexcept;

8 Effects: reset().

9 Postcondition: get() == nullptr

10 Returns: *this.

Therefore, just choose the one which makes its intent clearest for you. Personally, I prefer:

foo = nullptr;

Because it makes it more evident that we want the pointer to be null. As a general advice, however, try to minimize the situations where you need to explicitly reset a smart pointer.


Besides, rather than using new:

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

Consider using std::make_shared() when possible:

auto foo = std::make_shared<std::string>("foo");
Community
  • 1
  • 1
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 1
    Oop, thanks for the catch, I wasn't paying attention when writing out the string. Updated the question to reflect changes. – user1930581 Apr 22 '13 at 16:17
  • 1
    I would personally use nullptr for the same reason stated by @Andy Prowl. But try write your code so that the shared_ptr goes out of scope. – Trax Apr 22 '13 at 16:20
  • @TraxNet: I agree with your advice – Andy Prowl Apr 22 '13 at 16:22
  • Can you elaborate on why `make_shared` is preferred? – Mark B Apr 22 '13 at 16:44
  • 2
    @MarkB: Exception-safety and one less allocation: for a more detailed discussion, see for instance [this answer](http://stackoverflow.com/a/14837300/1932150). – Andy Prowl Apr 22 '13 at 16:46
  • 7
    Ok, three years have gone by. Hope someone will read this...I noticed, that your citation of the standard is for `unique_ptr` and also that there is no `operator=(nullptr_t)` method for `shared_ptr`. Is it correct, that `nullptr` will be casted to `unique_ptr` and then to `shared_ptr` if I use `foo = nullptr`? – mdr Jun 13 '16 at 07:48
  • 5
    I'm baffled that an answer that talks about *a different class* without ever even acknowledging it is so highly upvoted. As @mdr said, and [r0ng showed](https://stackoverflow.com/a/46577399/2757035) (albeit without enough detail or attention to optimisation), `shared_ptr` does not have an `operator=(nullptr_t)`, so assigning `nullptr` to it requires a conversion. We can probably show that it doesn't matter in optimised builds, but to say "_The two alternatives are absolutely equivalent, in the sense that the second form (foo = nullptr) is defined in terms of the first one"_ seems totally wrong – underscore_d Oct 22 '18 at 23:00
17

I would prefer reset() as it signals the intent. However, try to write your code such that you do not need to explicitly clear a shared_ptr<>, i.e. ensure that a shared_ptr<> goes out of scope when you would otherwise clear it.

underscore_d
  • 6,309
  • 3
  • 38
  • 64
Walter
  • 44,150
  • 20
  • 113
  • 196
  • Could you tell the reason behind the advice against clearing the pointer? Is there any performance hit or "just" not having a null pointer around? – Robert F. Jun 01 '17 at 09:40
  • 3
    @RobertF. No, there is no performance hit. Just, that the need for frequently clearing the pointer may indicate a design flaw. – Walter Jun 01 '17 at 13:53
5

They have a bit different if you use https://godbolt.org/ to check
by using gcc(7.2)
foo.reset(); generates assembly code

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

however, foo = nullptr; generates

  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()

It creates a shared pointer with nullptr, assign the newly created object to the variable and calls destructor to destory string.

Since I don't know how to check what happened in the function reset(). Can not see which is faster.

r0n9
  • 2,505
  • 1
  • 29
  • 43
  • 18
    If you compile both with [-O2](https://godbolt.org/g/bw6vQQ) you will see that there's no difference in a release build. – RandomGuy Jan 24 '18 at 16:50
-1

Generally, smart pointers can handle themselves. But if you need a solution, the reset() is, in my opinion, your best bet.

Xaltar
  • 1,688
  • 14
  • 22
  • 8
    Just stating an opinion does not answer the question, which crucially included a request for reasoning: "**Is there any real difference, or are there advantages/disadvantages to either approach?**" – underscore_d Oct 22 '18 at 22:56