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
vectors or other data structures that uses value semantics, and
unique_ptrs 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_ptrs, 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
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
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.