Smart Pointers Are Quite Good
Filed Under: Programming
The concept of the smart pointer is not particularly new. It’s probably existed for as long as
free have been inspiring programmers to invent increasingly elaborate garbage collection systems instead of manually managing the heap memory they use. In fact it’s quite easy to create some form of smart pointer without realizing you’re replicating the pattern. All you do is write a class which wraps up a pointer to a dynamically-allocated block of memory. Maybe it prevents other parts of the codebase coming in and pointing that pointer at something else without freeing the memory. Maybe it calls
delete on the pointer in the destructor. Voila. You’ve written a ‘smart’ pointer: an abstraction which automates or simplifies resource management.
If this is all a bit alien to you, you might be fairly new to C++ and still getting used to how pointers and manual memory management work in general. You also might not be up to speed on how the language standard has evolved in recent years. One nice evolution I’ve recently started taking advantage of is the addition of smart pointer class templates to the standard library.
std::unique_ptr helps with exclusive-ownership resource management.
std::unique_ptr<int> p(new int) makes wrapper around a pointer to a dynamically-allocated
int. Nobody else can point to that
int, and certainly nobody else can delete it out from underneath
p goes out of scope and its destructor is called it deallocates the
int so you don’t have to remember to call
delete yourself to avoid a resource leak. The overhead of using a
unique_ptr instead of a raw
int*? Negligible. The maintainability overhead of using a raw pointer? Definitely not negligible. This kind of smart pointer receives a big thumbs-up from me, and having a nice implementation right there in the standard library makes it hard to defend not making use of it2.
std::shared_ptr, on the other hand, is for shared-ownership resource management. Multiple
shared_ptr instances can point to the same block of data without the programmer needing to worry about someone deleting it before everyone else has finished with it. In order to do this it introduces reference-counting into the mix, so this time, yes, there is some overhead. That overhead, however, is book-keeping you’d probably be doing anyway. When a
shared_ptr’s destructor is called, the reference count variable it shares with the other
shared_ptrs that point to the same resource is decremented. If the reference count hits zero, it deallocates the resource.
There’s one more.
std::shared_ptr have a scrawny sibling:
std::weak_ptr. It acts like a
shared_ptr, but doesn’t participate in shared ownership. When pointed at a resource, the weak pointer shares a reference count variable with any
shared_ptrs that also point to that resource. Unlike the
shared_ptr it doesn’t modify the reference count; it just looks at it. This means it can tell when it’s dangling - when it points at memory that’s been freed - because the reference count is 0. Useful!
The main takeaway here is that I like these things, and I think should too. Once you’re used to them they’re very useful tools. If you’re still not convinced, my next post will demonstrate a use-case I found for
weak_ptr in a spare-time project that might tip the scales.