C++11: Using std::unique_ptr as a class member: initialization, move semantics and custom deleters
Updated 23rd February 2013: Fixed typos in the code, incorrect use of const, clarified the use of the terms “initialization” and “assignment” to be semantically correct, corrected/clarified references to “constructor” and “assignment operator”, added an explanation of the default copy assignment operator and unique_ptr’s private copy constructor and copy assignment operator.
Updated 12th May 2013: Fixed a factual error regarding standard functions not working as custom deleters in all scenarios, fixed a factual error stating that std::shared_ptr doesn’t support custom deleters (it does), and corrected references to boost
which should have referred to std
. Expanded the custom deleter section with example cases of new’ing your own resource classes or fetching resources from an external API or factory function. Added text explaining the advantages of using the function object idiom for custom deleters.
The std::unique_ptr class is C++11’s replacement for the flawed std::auto_ptr smart pointer from C++03. std::unique_ptr provides single-ownership pointer semantics and is therefore especially useful when dealing with resource handles which should belong to a single object, and its custom deleter feature (which allows the call to delete
to be replaced with an arbitrary function call when the object is destroyed, such as CloseHandle
or IUnknown::Release
) is great for ensuring resources are closed properly before their handles are freed.
I’m not going to explain how std::unique_ptr or move semantics work here – see the References section at the bottom for some introductory links – rather, I’m going to specifically look at how std::unique_ptr should be used in a non-copyable class (that is, one with a private, or – in C++ 11 – deleted, copy constructor), and specifically, how to make sure the resource is freed exactly and only once.
The Problem
A common design pattern is to declare your resource objects as members of the application class and load resources into them when the application starts. For example, somewhere in your application class you will have a member:
ResourceClass resource;
and in your startup code:
resource = ResourceClass("NameOfResource");
If you’re using std::unique_ptr to store the resource handle in ResourceClass, and it is using the default copy assignment operator, you’ve immediately got a problem on your hands. Consider the class:
class ResourceClass { private: std::unique_ptr<char []> resource; public: ResourceClass() {} ResourceClass(const char *resourceName) { char *data; // do something that loads some binary data into 'data' resource = std::unique_ptr<char []>(data); } };
As long as the instance of ResourceClass
isn’t copied, it works correctly: the constructor loads a file or some other block of data and assigns its pointer to resource
. When the object goes out of scope, resource
‘s destructor is called, which calls delete []
on its pointer and frees the memory used by the resource.
If we initialize the object in our application as follows:
ResourceClass resource("NameOfResource");
no copy is made so there is no problem. In all modern C++ compilers, this also works and is considered to be direct initialization, not a copy:
ResourceClass resource = ResourceClass("NameOfResource");
since the copy that would normally be made is elided by the compiler. This is good news for using factory patterns because the following will also work:
ResourceClass LoadSomething(const char *resourceName) { return ResourceClass(resourceName); } ... ResourceClass resource = LoadSomething("NameOfResource");
Here the compiler elides the temporary copy that would normally be made from the return value of LoadSomething()
.
Unfortunately, C++ does not allow initialization of class members (except for const
members) at the time of their declaration, so we must set them up by assigning values to them afterwards:
// in application class definition (initializes the object with its default constructor) ResourceClass resource; // in startup code (perhaps the application class constructor) (assigns value) resource = ResourceClass("NameOfResource");
Now, the use of the assignment operator (=
) causes a copy of the temporary object on the right-hand side of the assignment to be made, and attempts to store it in resource
. This invokes ResourceClass
‘s copy assignment operator, which, since we have not defined one explicitly, is the default implementation which simply calls the copy assignment operator for each member in the object. To prevent two instances of std::unique_ptr pointing to the same address, its copy assignment operator is declared private, and is therefore inaccessible to other classes. Therefore, this code (correctly) fails to compile.
The same is also true for std::unique_ptr‘s copy constructor. For example:
std::unique_ptr<int> first(new int); // OK std::unique_ptr<int> second = first; // Fails, copy constructor is private std::unique_ptr<int> third(first); // Fails, copy constructor is private std::unique_ptr<int> fourth; fourth = first; // Fails, assignment operator is private
Move Semantics To The Rescue
Assuming we don’t want to be able to copy the resource but only transfer ownership of which object manages it, the first step is to prevent the object from being copied by explicitly defining a copy constructor and a copy assignment operator, and making them private so that they can’t be called:
class ResourceClass { ... private: // No copying or copy assignment allowed of this class or any derived class ResourceClass(ResourceClass const &); ResourceClass &operator=(ResourceClass const &); ... };
If your compiler supports it (Visual C++ 2010 and 2012 don’t), you may prefer the C++11 delete semantic which specifies a default constructor should not be compiled in:
class ResourceClass { ... public: ResourceClass(ResourceClass const &) = delete; ResourceClass &operator=(ResourceClass const &) = delete; ... };
Now we will define a move assignment operator. The purpose of this is to transfer ownership of the pointer stored in the std::unique_ptr of the object being moved to the new object and is implemented as follows:
class ResourceClass { ... public: ResourceClass &operator=(ResourceClass &&o) { if (this != &o) { resource = std::move(o.resource); } return *this; } };
This directs the compiler to perform a move rather than a copy when =
is used with a temporary object on the right-hand side. The if
guards against self-assignment.
Although our particular problem does not call for it, it’s usually a wise idea if you need a constructor or assignment operator to define both (see ‘The Rule of Three‘), so we create a move constructor as well:
class ResourceClass { ... public: ResourceClass(ResourceClass &&o) : resource(std::move(o.resource)) {} ... };
Note that we have not used const
in the function signatures for the move constructor or move assignment operator. This is because the temporary object must be modifiable so that its std::unique_ptr can have nullptr
assigned to it.
When a temporary (and only a temporary – ie. one on the right-hand side of an assignment that has been generated by a function or constructor call and not a reference to an already-existing object in another variable) ResourceClass
object is assigned from now, the new std::unique_ptr is initialized with the pointer in the std::unique_ptr in the temporary object, but crucially, the pointer in the temporary object is set to nullptr
(by the new std::unique_ptr‘s move assignment operator invoked as a result of using std::move()
in our move assignment operator), preventing the memory being pointed to from being freed (delete
‘d) when the temporary object goes out of scope (when the assignment statement completes).
Now let’s go back to our original code:
// in application class definition ResourceClass resource; // in startup code (perhaps the application class constructor) resource = ResourceClass("NameOfResource");
This will now work as desired. The assignment causes ResourceClass
‘s move assignment operator to execute and ownership of the pointer in the std::unique_ptr is transferred correctly. The std::unique_ptr in the temporary object is nulled, the object goes out of scope, and no memory is freed, leaving it intact for use by resource
.
Custom Deleters when std::unique_ptr is a class member
We could have avoided using std::unique_ptr and having to mess around with move semantics altogether above, instead plumping for std::shared_ptr which will allow copies and only delete
the owned pointer when the last copy of std::shared_ptr goes out of scope. Both std::unique_ptr and std::shared_ptr support custom deleters so the correct choice really depends on what kind of ownership semantics you want: use std::unique_ptr when a resource should only be owned by one object, and std::shared_ptr when ownership can be shared among several objects.
Imagine we have a resource type Resource
which must be closed by calling its Close()
method before being freed. You can handle this scenario as follows:
void closeResource(Resource *res) { res->Close(); } class ResourceClass { private: std::unique_ptr<Resource, void (*)(Resource *)> resource; ... public: ResourceClass(...) : resource(new Resource, closeResource) { // Do something to populate 'resource' if needed } };
The second template parameter to std::unique_ptr specifies the function pointer type for the custom deleter. This must always be a function which takes the raw pointer to the resource as a single argument and returns void.
This is fine if you have written the Resource
class yourself or it is some other object that can be instantiated with new
directly, but sometimes we have to use external libraries, APIs or factory functions which return instances of a resource via a pointer. In these cases, we cannot immediately initialize resource
in ResourceClass
‘s constructor initializer list. However, if we don’t provide an initialization, you will get a compile error such as:
error C2338: unique_ptr constructed with null deleter pointer
or similar, depending on your compiler (this error is from Visual Studio).
Consider the case where we use an external library factory function CreateResourceFromAPI(ExternalResource **)
to create a resource of type ExternalResource
, again defined in the external library. ExternalResource
provides a Close
function so we want to use a custom deleter. We can work around the problem like this:
void closeExternalResource(ExternalResource *res) { res->Close(); } class ResourceClass { private: typedef std::unique_ptr<ExternalResource, void (*)(ExternalResource *)> ResourceType; ResourceType resource; ... public: ResourceClass(...) : resource(nullptr, closeExternalResource) { // Create resource ExternalResource *data; CreateResourceFromAPI(&data); resource = ResourceType(data, closeExternalResource); } };
(the typedef
is optional but good for readability and reducing typing errors)
As you can see, this requires resource
to be initialized with no pointer in the constructor initializer list, and assigned to afterwards in the constructor itself. This is – in my opinion – a bit messy.
Function object idiom
Instead, we can use the function object idiom. This pattern defines a type with a single operator()
which performs the close operation on the resource. It is defined like this:
// Function object to close a resource struct CloseResource { void operator()(Resource *res) const { res->Close(); } };
For the case where we new
up the resource ourselves, the first example can be re-written as follows:
// The resource wrapper class class ResourceClass { private: std::unique_ptr<Resource, CloseResource> resource; ... public: ResourceClass(...) : resource(new Resource) { // Do something to populate 'resource' if needed } };
The example using the factory function can be re-written like this:
struct CloseExternalResource { void operator()(ExternalResource *res) const { res->Close(); } }; class ResourceClass { private: typedef std::unique_ptr<ExternalResource, CloseExternalResource> ResourceType; ResourceType resource; ... public: ResourceClass(...) { // Create resource ExternalResource *data; CreateResourceFromAPI(&data); resource = ResourceType(data); } };
The difference here between using the function object idiom and a standard function is that we have provided a concrete type as the second template parameter (the function object), and as such, when we initialize the std::unique_ptr we don’t need to specify the custom deleter function as the 2nd regular argument anymore. This saves us from having to initialize the resource in the constructor initializer list when we don’t need to, and from typing the function name every time we place a resource into a std::unique_ptr.
This will now all work as expected: Close()
will be called on the Resource/ExternalResource
object pointed to by std::unique_ptr when the ResourceClass
object goes out of scope. If you combine this with the move semantics above, you can return instances of ResourceClass
itself from your own factory functions and move them to members of your application class using regular assignment, safe in the knowledge that Close()
will not be called when the temporary objects go out of scope – only when the application class does.
Conclusion
There are other ways to solve the problem of correctly destroying a resource exactly once without using std::unique_ptr or move semantics: traditional reference counting, std::shared_ptr, boost::intrusive_ptr (especially useful for COM objects and other objects with an internal reference count), and so on. Each has their pros and cons, and if you really do need copies of the object the techniques outlined here won’t work for you, but in most cases a resource is owned by a single object instance, and C++11 provides an efficient if not entirely neat solution to this in the form of std::unique_ptr and move semantics.
I’m a software developer with very limited work capacity due to having the debilitating illness M.E. – please read my article Dying with M.E. as a Software Developer and donate to the crowdfund to help me with my bucket list if you found this article useful. Thank you so much!
References
CProgramming.com: Move semantics and rvalue references in C++11
InformIT: Using unique_ptr, Part I
InformIT: Using unique_ptr, Part II
SmartBear: The Biggest Changes in C++11 (and Why You Should Care)
“Unfortunately, this doesn’t work when std::unique_ptr is a class member.” <- Why, in your opinion would this not work? Granted the way it is written above – it will not work, but there is no problem in using a function as a custom deleter in a unique_ptr which is a class member.
Now the constructor of ResourceClass seems to be using a stack object and stores that in the unique_ptr – so this will first of all not compile, and if you modify the code to use &data – the data object will go out of scope by the end of the constructor and you will have undefined behavior. So unless there are some typedefs here which are not shown, the code should be Resource* data = new Resource; but then this should be put in the constructor initializer list, and thus you can do
ResourceClass() : resource(new Resource, closeResource) {
//Populate resource
}
Also I'm wondering why you advocate boost::shared_ptr, when you're already using std::unique_ptr – why not just use std::shared_ptr.
Now when it comes to boost::shared_ptr, it does support custom deleter.
Thank you for this comment which has taken me far too long to reply to. The problem with using the constructor’s initializer list to create the resource happens when you can’t immediately new up the resource and instead have to fetch it from an API/factory function. I have re-written the custom deleters section of the article to fully explain this and with examples of using the constructor initializer list for when the resource can be new’ed directly.
The stack object was a typo (the code was based on SimpleFMOD which does indeed use a typedef as you suggested, I forgot to change this – my mistake), as were the references to boost which I have now changed to std. I’ve also changed the text to reflect that std::shared_ptr does support custom deleters, I don’t know why I thought it didn’t.
Please let me know if there are any other inaccuracies and thanks for the feedback 🙂
Wouldn’t
be better than
?
Hi Katy. Good stuff. If it helps, I too in older C++ days used the private constructor/operator= idiom to prevent class instances from being copied (to prevent value copying semantics, etc).
Modern C++11 includes the awesome new feature of deleting the default provision of copy constructors and operator= from a class, guaranteeing its use under reference semantics.
class Blah
{
Blah& operator=(const Blah& blah) = delete;
Blah(const Blah& blah) = delete;
};
This ensure reference semantics when using class Blah instances.
HTH
R
Katy, really great article, it seems quite a bit of the description of the generic Resource class is about making sure that is it not copyable – due to restrictions on field members that are move only. I have a similar resource class that uses a boost::asio::io_service object (this is not copyable either). I also have other utility classes that have a std::condition_variable member (also not copyable) to aid in the inter thread communication.
In general my classes have a thread member function. Before I read your article I used to store these move only objects in std::shared_ptr members – so I could work around the restrictions enforced by move only members, now I’m thinking that it might be better to simply resource classes move only, do you know of an approach similar to the one I am trying to achieve with condition variables and boost asio ioservice objects (which I use for thread timers)
All the best and thanks for writing such a high quality set of articles about my 2nd favourite class unique_ptr – after vector of course!
John
Hey John, glad you enjoyed the article! To be straight to the point I have no experience of using Boost asio at all so I can’t help you on this one, sorry!
Your CloseResource functor doesn’t actually delete the Resource after Close’ing it. Surely that’s a memory leak?
Wrong. I do not think you understand how the smart pointers work. He does not have to delete the unique_ptr because the unique_ptr will delete itself after it calls his functor. I wish people would do some research before making stupid comments.
Would you be able to help me with this problem?
http://stackoverflow.com/questions/36813517/implementing-move-constructor-and-assignment-with-unique-ptr
In my sense, simply stop to bloat and obfuscate programs with elite standard and go real c++ with dynamic instance creation. No need copy, move, ect. You can move yourself your pointer if needed. The c++ langage need evolution to give static inheritance, to give pointer to base class in multiple virtual inheritance, and other fondamental improvement like improved granularity access, member initialization at construction, more control on exception in constructor/destructor, an initializ function optionaly auto called after instance creation, but not these obsucated update from std. Actualy and for a time, the c++ seem to go on bad road.