An overview on smart pointers
My last blog post in 2013 was about the pointer in C++, and how most of its daily usages in C++ is now being replaced by classes replacing or managing the pointer. The last case, the RAII like objects called smart pointers is the topic of this post. I want to give an overview over the choices one can make when using smart pointers.
As I studied last year boost, Qt and wxWidgets closer, I saw, that all of them have their own implementations of smart pointers. Also, C++11 brings its own set of two classes of smart pointers. So, with C++11, smart pointers have arrived in the standard, and everyone using C++11 automaticly has 2 different good options for managing memory allocated with new.
Should you use smart pointers?
I think its good to discuss this point first, when you should use smart pointers, and when not. Smart pointers are only useful when used with new, or the corresponding make functions (make_shared and make_unique in C++14 f.e.). So, a smart pointer is only needed, when you use new or other means of dynamic memory allocation. In my opinion, you should prefer to allocate variables on the stack, so when refactoring code (to C++11), you should always ask yourself, if this new is needed, or could be replaced with an object on the stack. When you need to use new, you should always use a smart pointer in my opinion. Also some smart pointers offer a custom deleter, which is handy if you have an object that is either not allocated by new and/or needs to be freed by calling a special function.
A (not so) short overview on smart pointers
As mentioned, with C++11 two new classes came to the C++ Standard, introducing shared_ptr and uniqe_ptr for the means of managing memory allocated with new. Previously there has been std::auto_ptr in the standard, which is now deprecated.
The idea to use smart pointers is at least 20 years old, as the documentation of boosts Smart Ptr Library shows. Also boost has been the go to place for smart pointers before C++11, and for example wxWidgets has copied their Smart Pointer version from boost in 2009. Lets compare some of the implementations:
|Name||copyable||moveable||custom deleter||can release ownership||comment|
|std::unique_ptr||no||yes||yes (by policy)||yes|
|poco::AutoPtr||yes||no (C++03)||no||no||A certain interface must be provided by T.|
|poco::SharedPtr||yes||no (C++03)||yes (by policy)||no|
|dlib::shared_ptr||yes||no (C++03)||no||no||not threadsafe|
|ACE::Value_Ptr||yes (but copies the pointee)||no (C++03)||no||no|
|Loki::SmartPtr||yes per default||maybe over policies, else no||no||no||
mostly policy based,
|Loki::StrongPtr||yes per default||see above||yes||no||
see above and Lokis Smart Pointer Page
A few words on this table. Most all libraries have implemented smart pointers way before C++11, so move constructors are not implemented and move behavior in general is not documented. Shared classes do share the pointer through different instances through RefCounting. I do have experience with using the standard version, boost, Qt and wxWidgets, the other data is taken from the documentation of those libraries.
I think that is enough for a first overview. Many other libraries probably have written their own versions, some even might have oriented their solution on boosts Smart Ptr library as wxWidgets did, and also the C++11 smart pointers have their roots in the boost versions. I didn't list platform or library specific smart pointers (except poco::AutoPtr). Also some older libraries model std::auto_ptr. A special case is the smart pointer implementation from loki, as its very versatile and can be configured via policy based design. Per default its shared, but you can create/use a non shared policy.
So, smart pointers can be classified into (mainly) 4 categories:
- shared (Refcounting usually)
- intrusive / interfacebased
- framework specific
Scoped and unique smartpointers
This is the most common class, and in my opinion also the sort of smart pointer you should mostly use, and only if your specific use case REALLY breaks the case for this type, think about using any of the other types. The scoped pointer ensures, that an allocated object is destroyed when its scope ends. Interestingly Poco seems to lack this type of smart pointer.
A special case is std::unique_ptr, as it does not have the same behavoir as the scoped pointers. It is allowed to escape its scope through a move. This makes it possible to have a container of unique_ptr, or f.e. a factory returning them, also C++14 will add make_unique. With the addition of make_unique in C++14 also the use of new (and also delete) is handled in the background. So the need for directly using new and delete is (mostly) gone.
Non owning pointers to scope or unique pointeres still need to be raw pointers. There is a proposal called exempt_ptr, which could take this role.
Shared smart pointers
Some times you need the ability to share a pointer between classes and objects, and so smart pointers have a shared type, which ensures through refcounting, that the held pointer stays valid till the last instance is destroyed. So each time a copy of the first shared pointer is destroyed, the refcount goes down, if it ever reaches 0 the object is destroyed.
Ever? Yes. That is one of the problems with this implementation, there can occur a cyclic dependency, that prevents one or more smart pointers from ever being destroyed. For example if you would model a parent child relation with two shared pointers. This is why (most) shared pointer implementations today also bring a weak_ptr, which can be converted into a shared pointer when needed. The weak_ptr only holds a weak link to the original object. This is usually with two counters implemented, one for strong references (e.g. actual copies) and one for weak pointer objects.
The allocation of the actual object can be a bit special with shared pointers, as also the variable for refcounting should be allocated on the heap. This is a very good use case for placement new, as it allows to only have one call to new allocating the space for the counters and the actual object. This is only possible if its done in a make_shared like function, not inside of a constructor from a shared pointer type. Interestingly I'm only aware of std::make_shared and boost::make_shared, the other shared pointer implementations don't mention special make functions.
But shared pointers are only good in a few places. You should be aware, that this is more or less a globally shared variable, most implementations are not threadsafe for accessing the held pointer, some might not even have threadsafe reference counting. Only using a shared_ptr<const T> should be viewed as safe, as it only shares a const object which can not be altered. Also const methods are thread safe in C++.
Intrusive / Interface based smart pointers
I didn't list boost::intrusive pointer, and some other frameworks have similar solutions. Also poco::AutoPtr belongs into this class. This class usually holds a pointer which has some internal mechanism for refcounting. It can be used for interfacing with COM or other APIs and c libraries. Also some frameworks offer interfaces which you need to implement for a certain type in order to use the smart pointer interface. This is usually a function/method for incrementing and decrementing, and maybe release.
Framework specific (smart) pointer classes
There do exist a few smart pointer classes which are framework specific. For example QPointer is designed to hold a QObject derived instance, it does not call delete when it is destroyed, but when the QObject is destroyed it will no longer point to it. Also Qt offers QSharedDataPointer, a shared pointer which allows implicit sharing, in order to use QSharedPointer you have to derive from QSharedData. Also CComPtr from the ATL can be seen either as an intrusive variant or a framework specific smart pointer.
Refactoring towards smart pointer usage
So, now where an overview is given, and also a little bit about the correct usage is written, I'd like to focus on refactoring. There is a lot of code which currently does not use smart pointers. Even newly written SDKs some times do not use them, but mostly use delete correctly. One of the advantages of smart pointers is, that they ensure due to RAII, that the actual object is deleted. When using a raw pointer, you need to have a delete for every possible exit point, and still an exception will lead to a memory leak. Smart pointers will also free the memory if an exception occurs.
I'd like to share a little story for this. A few years ago there was an SDK for a certain mobile platform released, and as OO code, there was a need to use new on all kinds of objects. I was interested in writing apps for this platform, so I visited a public event for app developers for this SDK. I even got a phone! After the event there was some socializing, and I got to talk to a person belonging to the devteam for the C++ API. I asked him why they didn't use smart pointers, instead of letting the users produce all kind of memleaks on their platform. The answer was "What are smart pointers?" So, it turns out, they used C++, and never had heard about smart pointers.
So, lets say, for our industry, smart pointers are not standard, and there is some amount of code that needs refactoring. You have to be very careful in refactoring a simple pointer into a smart pointer. Memberpointers within a class usually can be converted, but you have to find out if you can make the pointer a unique/scoped pointer, or if it is shared amongst different objects, requiring to make it a shared pointer. Use features of your IDE like show all usages, to see if and how you can convert a simple pointer to a smart pointer. Some pointers are just non owning pointers, this is fine if the pointer it self is either pointing to an non-newed object or one held in a unique or scoped pointer. Shared pointers usually have a weak pointer type for this usage.
With scoped pointers in (member)functions you have to be a bit more careful. I've seen last year a very hard to find problem with this. Turning a new allocation in a larger function into a scoped ptr did not result into a crash, when the program was still accessing the value. Instead it seemed to work just fine for some time, and things did not even crash, the application was just showing weird values. Interestingly this triggered far earlier in debug mode. Also, a scoped pointer can not be returned from a factory function, but unique_ptr can use move semantics.
Custom deleters & smart arrays
The above table shows, that some smart pointer classes offer custom deleters, and some do not. Also boost does not support this feature for scoped_ptr. Maybe because you easily could implement this for yourself, simply a class wrapping a pointer to T and doing the correct thing in the destructor. This class then can be directly used on the stack or be wrapped into a shared smart pointer.
A special case are arrays allocated with new. boost has its own classes for this (scoped_array and shared_array), also boost::shared_ptr has traits to detect array usage, and correctly free it with delete  instead of delete. Smart pointers that have a custom deleter can be used with smart arrays and an array deleter.
So which smart pointer should you prefer?
As I already wrote, use the stack if possible, and if you need a smart pointer its simply:
std::unique_ptr or a Scoped Ptr > shared pointer > intrusive pointer > framework specific pointer classes
This leaves the question, which implementation you should favor using. And I think, that is something which has to be decided on the local needs of the code you use. For libraries I think that the standard implementations are good, but that if you need backwards compatibility to < C++11, boost is as good. But, if you already use a framework such as Qt or wxWidgets, you also can use their implementations.
For shared smart pointers you always should prefer to call the make_shared function (if the implementation offers one), the standard offers with C++14 also for unique_ptr a make_unique function.
Disadvantages of smart pointers
There two things which can be seen as disadvantages, actually its only one little overhead and one issue with the standardization of smart pointers.
First, with C++11, smart pointers (and some other nice things) are now part of the standard, before C++11 this was a very good argument to use boost. I think that boost has many other advantages, but smart pointers are a good door opener, especially in more restricted areas, where you have to get libraries or frameworks approved before using them.
Second, there is a little overhead. Shared pointers add usually two counting variables to the memory needs of your class, while unique_ptr is just a wrapper. This is a tiny overhead in memory usage, which is largely outperformed by the security smart pointers offer. Only a few embedded devices should not be able to afford this little overhead. Also the little overhead in allocation should be fine for most applications, if you're application is speed critical, you might want to measure if smart pointers have any impact to your system.
Join the Meeting C++ patreon community!
This and other posts on Meeting C++ are enabled by my supporters on patreon!