74

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
  • 27,453
  • 22
  • 81
  • 151
user1930581
  • 1,235
  • 1
  • 11
  • 22
  • 1
    `foo = {}` is also an option! – Luc Danton Apr 22 '13 at 18:46
  • 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

4 Answers4

83

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
  • 114,596
  • 21
  • 355
  • 432
  • 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
  • 6
    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
  • 2
    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
15

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
  • 5,331
  • 3
  • 29
  • 56
Walter
  • 40,885
  • 16
  • 97
  • 176
  • 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
4

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.

r0ng
  • 1,669
  • 23
  • 33
  • 10
    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,530
  • 13
  • 22
  • 4
    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