The world that Bjarne lives in is very... academic, for want of a better term. If your code can be designed and structured such that objects have very deliberate relational hierarchies, such that ownership relationships are rigid and unyielding, code flows in one direction (from high-level to low-level), and objects only talk to those lower in the hierarchy, then you won't find much need for shared_ptr
. It's something you use on those rare occasions where someone has to break the rules. But otherwise, you can just stick everything in vector
s or other data structures that uses value semantics, and unique_ptr
s for things you have to allocate singly.
While that's a great world to live in, it's not what you get to do all of the time. If you cannot organize your code in that way, because the design of the system you're trying to make means that it is impossible (or just deeply unpleasant), then you're going to find yourself needing shared ownership of objects more and more.
In such a system, holding naked pointers is... not dangerous exactly, but it does raise questions. The great thing about shared_ptr
is that it provides reasonable syntactic guarantees about the lifetime of the object. Can it be broken? Of course. But people can also const_cast
things; basic care and feeding of shared_ptr
should provide reasonable quality of life for allocated objects who's ownership must be shared.
Then, there are weak_ptr
s, which cannot be used in the absence of a shared_ptr
. If your system is rigidly structured, then you can store a naked pointer to some object, safe in the knowledge that the structure of the application ensures that the object pointed to will outlive you. You can call a function that returns a pointer to some internal or external value (find object named X, for example). In properly structured code, that function would only be available to you if the object's lifetime were guaranteed to exceed your own; thus, storing that naked pointer in your object is fine.
Since that rigidity is not always possible to achieve in real systems, you need some way to reasonably ensure the lifetime. Sometimes, you don't need full ownership; sometimes, you just need to be able to know when the pointer is bad or good. That's where weak_ptr
comes in. There have been cases where I could have used a unique_ptr
or boost::scoped_ptr
, but I had to use a shared_ptr
because I specifically needed to give someone a "volatile" pointer. A pointer who's lifetime was indeterminate, and they could query when that pointer was destroyed.
A safe way to survive when the state of the world is indeterminate.
Could that have been done by some function call to get the pointer, instead of via weak_ptr
? Yes, but that could more easily be broken. A function who returns a naked pointer has no way of syntactically suggesting that the user not do something like store that pointer long-term. Returning a shared_ptr
also makes it way too easy for someone to simply store it and potentially prolong the life-span of an object. Returning a weak_ptr
however strongly suggests that storing the shared_ptr
you get from lock
is a... dubious idea. It won't stop you from doing it, but nothing in C++ stops you from breaking code. weak_ptr
provides some minimal resistance from doing the natural thing.
Now, that's not to say that shared_ptr
can't be overused; it certainly can. Especially pre-unique_ptr
, there were many cases where I just used a boost::shared_ptr
because I needed to pass a RAII pointer around or put it into a list. Without move semantics and unique_ptr
, boost::shared_ptr
was the only real solution.
And you can use it in places where it is quite unnecessary. As stated above, proper code structure can eliminate the need for some uses of shared_ptr
. But if your system cannot be structured as such and still do what it needs to, shared_ptr
will be of significant use.