234

Ok, so the last time I wrote C++ for a living, std::auto_ptr was all the std lib had available, and boost::shared_ptr was all the rage. I never really looked into the other smart pointer types boost provided. I understand that C++11 now provides some of the types boost came up with, but not all of them.

So does someone have a simple algorithm to determine when to use which smart pointer? Preferably including advice regarding dumb pointers (raw pointers like T*) and the rest of the boost smart pointers. (Something like this would be great).

Community
  • 1
  • 1
sbi
  • 204,536
  • 44
  • 236
  • 426
  • See also [std::auto_ptr to std::unique_ptr](http://stackoverflow.com/q/3451099/14065) – Martin York Jan 02 '12 at 23:56
  • 2
    I am really hoping someone comes up with a nice handy flowchart like [this STL selection flowchart](http://stackoverflow.com/questions/471432/in-which-scenario-do-i-use-a-particular-stl-container). – Alok Save Jan 03 '12 at 07:41
  • 1
    @Als: Oh, that's indeed a nice one! I FAQized it. – sbi Jan 03 '12 at 09:10
  • 6
    @Deduplicator That's not even close to being a duplicate. The linked question says "When should I use *a* smart pointer" and this question is "When do I use *these* smart pointers?" i.e. this one is categorising the different uses of the standard smart pointers. The linked question does not do this. The difference is seemingly small but it's a big one. – Rapptz Jun 05 '14 at 03:36

4 Answers4

185

Shared ownership:
The shared_ptr and weak_ptr the standard adopted are pretty much the same as their Boost counterparts. Use them when you need to share a resource and don't know which one will be the last to be alive. Use weak_ptr to observe the shared resource without influencing its lifetime, not to break cycles. Cycles with shared_ptr shouldn't normally happen - two resources can't own each other.

Note that Boost additionally offers shared_array, which might be a suitable alternative to shared_ptr<std::vector<T> const>.

Next, Boost offers intrusive_ptr, which are a lightweight solution if your resource offers reference-counted management already and you want to adopt it to the RAII principle. This one was not adopted by the standard.

Unique ownership:
Boost also has a scoped_ptr, which is not copyable and for which you can not specify a deleter. std::unique_ptr is boost::scoped_ptr on steroids and should be your default choice when you need a smart pointer. It allows you to specify a deleter in its template arguments and is movable, unlike boost::scoped_ptr. It is also fully usable in STL containers as long as you don't use operations that need copyable types (obviously).

Note again, that Boost has an array version: scoped_array, which the standard unified by requiring std::unique_ptr<T[]> partial specialization that will delete[] the pointer instead of deleteing it (with the default_deleter). std::unique_ptr<T[]> also offers operator[] instead of operator* and operator->.

Note that std::auto_ptr is still in the standard, but it is deprecated. §D.10 [depr.auto.ptr]

The class template auto_ptr is deprecated. [ Note: The class template unique_ptr (20.7.1) provides a better solution. —end note ]

No ownership:
Use dumb pointers (raw pointers) or references for non-owning references to resources and when you know that the resource will outlive the referencing object / scope. Prefer references and use raw pointers when you need either nullability or resettability.

If you want a non-owning reference to a resource, but you don't know if the resource will outlive the object that references it, pack the resource in a shared_ptr and use a weak_ptr - you can test if the parent shared_ptr is alive with lock, which will return a shared_ptr that is non-null if the resource still exists. If want to test whether the resource is dead, use expired. The two may sound similar, but are very different in the face of concurrent execution, as expired only guarantees its return value for that single statement. A seemingly innocent test like

if(!wptr.expired())
  something_assuming_the_resource_is_still_alive();

is a potential race condition.

Toby Speight
  • 23,550
  • 47
  • 57
  • 84
Xeo
  • 123,374
  • 44
  • 277
  • 381
  • 1
    In the case of no ownership, you should probably prefer references to pointers unless you need no ownership *and* resettability where references won't cut it, even then you might want to consider rewriting the original object to be a `shared_ptr` and the non owning pointer to be a `weak_ptr`... – David Rodríguez - dribeas Jan 02 '12 at 23:33
  • @Davig: References to pointers are not a good idea. What if they don't get set through the actual owning ptr but through a local or parameter to a function? – Xeo Jan 02 '12 at 23:46
  • 2
    I did not mean *reference to pointer*, but rather reference *instead of* pointer. If there is no ownership, unless you need resettability (or nullability, but nullability without being able to reset would be quite limited) you can use a plain reference rather than a pointer in the first place. – David Rodríguez - dribeas Jan 03 '12 at 06:30
  • 1
    @David: Ah, I see. :) Yeah, references aren't bad for that, I personally prefer them too in such cases. I'll add them. – Xeo Jan 03 '12 at 07:18
  • 1
    @Xeo: `shared_array` is an alternative to `shared_ptr` not to `shared_ptr>`: it cannot grow. – R. Martinho Fernandes Jan 03 '12 at 17:58
  • @R.MartinhoFernandes: `shared_ptr` is not specialized for arrays, you'll need to provide your own deleter and it needs to be just `shared_ptr`. – Xeo Jan 03 '12 at 18:01
  • Well, I meant it acts more like such an hypothetical specialization would (or a normal one with a `default_deleter`) than `shared_ptr>` :) I'd also add a note that with `weak_ptr` you can test if the pointer is dead with `expired` or if it's alive with `lock`. `expired` is not fit for testing if the object is alive (hence its name). – R. Martinho Fernandes Jan 03 '12 at 18:05
  • @R.MartinhoFernandes: Meh, finally came around to updating this answer. Amended with the info on how to test the availability of the resource. – Xeo Mar 19 '12 at 09:29
  • Raw pointers also allow for manual garbage collection. You can use `release` for unique_ptrs but that doesn't free up the memory. – JohnJohn Jan 31 '15 at 20:25
  • @JohnJohn: [`std::unique_ptr<>::reset()`](http://en.cppreference.com/w/cpp/memory/unique_ptr/reset) is the function to use to destruct the object and deallocate the memory the `unique_ptr` managed, and "garbage collection" implies a lot of things (e.g. groping around memory) that are not necessary in C++. `release` very deliberately doesn't free up memory because it's a way of saying you want to take ownership from the `unique_ptr` - there wouldn't be anything to take ownership of if `release` destructed/deallocated. – Tony Delroy Feb 10 '15 at 05:37
  • References cannot be rebound which can be problematic in copy semantics and therefore you can use `std::reference_wrapper`. Unfortunately, the latter is a bit dull since it is currently not possible to override `operator.` – Matthias Mar 28 '17 at 15:54
  • It's worth noting that the last example is not thread-safe. May not be an issue for a lot of codebases. – Gregory Currie Jun 09 '17 at 01:18
  • 1
    @GregroyCurrie: That's... exactly what I wrote? I said it's an example of a potential race condition. – Xeo Jun 09 '17 at 07:40
  • Sorry mate, I missed that. Long day. – Gregory Currie Jun 09 '17 at 10:57
127

Deciding what smart pointer to use is a question of ownership. When it comes to resource management, object A owns object B if it is in control of the lifetime of object B. For example, member variables are owned by their respective objects because the lifetime of member variables is tied to the lifetime of the object. You choose smart pointers based on how the object is owned.

Note that ownership in a software system is separate from ownership as we would think of it outside of software. For example, a person might "own" their home, but that doesn't necessarily mean that a Person object has control over the lifetime of a House object. Conflating these real world concepts with software concepts is a sure-fire way to program yourself into a hole.


If you have sole ownership of the object, use std::unique_ptr<T>.

If you have shared ownership of the object...
- If there are no cycles in ownership, use std::shared_ptr<T>.
- If there are cycles, define a "direction" and use std::shared_ptr<T> in one direction and std::weak_ptr<T> in the other.

If the object owns you, but there is potential of having no owner, use normal pointers T* (e.g. parent pointers).

If the object owns you (or otherwise has guaranteed existence), use references T&.


Caveat: Be aware of the costs of smart pointers. In memory or performance limited environments, it could be beneficial to just use normal pointers with a more manual scheme for managing memory.

The costs:

  • If you have a custom deleter (e.g. you use allocation pools) then this will incur overhead per pointer that may be easily avoided by manual deletion.
  • std::shared_ptr has the overhead of a reference count increment on copy, plus a decrement on destruction followed by a 0-count check with deletion of the held object. Depending on the implementation, this can bloat your code and cause performance issues.
  • Compile time. As with all templates, smart pointers contribute negatively to compile times.

Examples:

struct BinaryTree
{
    Tree* m_parent;
    std::unique_ptr<BinaryTree> m_children[2]; // or use std::array...
};

A binary tree does not own its parent, but the existence of a tree implies the existence of its parent (or nullptr for root), so that uses a normal pointer. A binary tree (with value semantics) has sole ownership of its children, so those are std::unique_ptr.

struct ListNode
{
    std::shared_ptr<ListNode> m_next;
    std::weak_ptr<ListNode> m_prev;
};

Here, the list node owns its next and previous lists, so we define a direction and use shared_ptr for next and weak_ptr for prev to break the cycle.

Peter Alexander
  • 50,304
  • 10
  • 114
  • 163
  • 3
    For the example of the binary tree some people would suggest using `shared_ptr` for the children and `weak_ptr` for the parent relationship. – David Rodríguez - dribeas Jan 02 '12 at 23:28
  • @DavidRodríguez-dribeas: It depends whether the Tree has value semantics or not. If people are going to be referencing your tree externally even once the source tree is destroyed then yes, shared/weak pointer combo would be best. – Peter Alexander Jan 02 '12 at 23:37
  • If an objects owns you and is guaranteed to exist then why not a reference. – Martin York Jan 02 '12 at 23:52
  • @LokiAstari: Possibility of null. I'll add that. – Peter Alexander Jan 02 '12 at 23:56
  • 1
    If you use reference, you cannot ever change the parent, which may or may not hinder the design. For balancing trees, that would hinder. – Mooing Duck Jan 03 '12 at 07:43
  • @MooingDuck, I really with there was a way to rebind references, for just this reason. – deft_code Jan 03 '12 at 16:41
  • 3
    +1 but you should add a definition of "ownership" on the first line. I often find myself having to clearly state that it's about life & death of the object, not ownership in a more domain-specific meaning. – Klaim Jan 03 '12 at 17:30
  • @Klaim: Good comment. I added a definition at the top. Cheers. – Peter Alexander Jan 08 '12 at 14:14
  • That is very good explanation , but I couldn't understand two things **1:** how is weak ptr different and **2:** what is problem of cycles here? , might be you could give me some motivational problem? e.g `struct ListNode { std::shared_ptr m_next; std::shared_ptr m_prev; // no weak ptr , now what? };` – Mr.Anubis May 14 '12 at 18:39
  • @SoulReaper: Cycles are a problem because `shared_ptr` uses reference counting and if two objects reference each other then they'll never decrement their ref counts. – Peter Alexander May 14 '12 at 21:40
  • "If the object owns you" - I can't get what does it mean, can you explain, pls? – Alecs Jun 12 '12 at 13:45
20

Use unique_ptr<T> all the time except when you need reference counting, in which case use shared_ptr<T> (and for very rare cases, weak_ptr<T> to prevent reference cycles). In almost every case, transferrable unique ownership is just fine.

Raw pointers: Good only if you need covariant returns, non-owning pointing which can happen. They're not terrifically useful otherwise.

Array pointers: unique_ptr has a specialization for T[] which automatically calls delete[] on the result, so you can safely do unique_ptr<int[]> p(new int[42]); for example. shared_ptr you'd still need a custom deleter, but you wouldn't need a specialized shared or unique array pointer. Of course, such things are usually best replaced by std::vector anyway. Unfortunately shared_ptr does not provide an array access function, so you'd still have to manually call get(), but unique_ptr<T[]> provides operator[] instead of operator* and operator->. In any case, you have to bounds check yourself. This makes shared_ptr slightly less user-friendly, although arguably the generic advantage and no Boost dependency makes unique_ptr and shared_ptr the winners again.

Scoped pointers: Made irrelevant by unique_ptr, just like auto_ptr.

There's really nothing more to it. In C++03 without move semantics this situation was very complicated, but in C++11 the advice is very simple.

There are still uses for other smart pointers, like intrusive_ptr or interprocess_ptr. However, they're very niche and completely unnecessary in the general case.

R. Martinho Fernandes
  • 209,766
  • 68
  • 412
  • 492
Puppy
  • 138,897
  • 33
  • 232
  • 446
  • Also, raw pointers for iteration. And for output parameter buffers, where the buffer is owned by the caller. – Ben Voigt Jan 02 '12 at 23:08
  • Hmm, the way I read that, it's situations which are both covariant return and non-owning. A rewrite might be good if you meant the union rather than intersection. I'd also say that iteration is worth special mention also. – Ben Voigt Jan 02 '12 at 23:11
  • 2
    `std::unique_ptr` provides `operator[]` instead of `operator*` and `operator->`. It's true that you still need to do bound checking yourself though. – Xeo Jan 02 '12 at 23:16
9

Cases of when to use unique_ptr:

  • Factory methods
  • Members that are pointers (pimpl included)
  • Storing pointers in stl containters (to avoid moves)
  • Use of large local dynamic objects

Cases of when to use shared_ptr:

  • Sharing objects across threads
  • Sharing objects in general

Cases of when to use weak_ptr:

  • Large map that acts as a general reference (ex. a map of all open sockets)

Feel free to edit and add more

Lalaland
  • 8,180
  • 3
  • 30
  • 47