One big change to modern C++ style that comes with C++11 is that you should never need to manually delete
(or free
) anymore, thanks to the new classes shared_ptr
, unique_ptr
and weak_ptr
.
Note that before C++11, C++ did have one smart pointer class – auto_ptr
. This was unsafe and is now deprecated, with unique_ptr
replacing it.
To use these classes, you'll need to #include <memory>
(and also add using namespace std;
or prefix with std::
).
unique_ptr
unique_ptr
simply holds a pointer, and ensures that the pointer is deleted on destruction. unique_ptr
objects cannot be copied.
It thus behaves very much like the now-deprecated auto_ptr
behaved – the problem with auto_ptr
was that it was aiming to work what unique_ptr
does, but unable to do so properly when it was defined, back before C++11 was invented with features like move-constructors, and thus it was unsafe.
As example of how unique_ptr
is used is as follows – say we have the following:
struct MyClass {
MyClass(const char* s);
void methodA();
};
void someMethod(MyClass* m);
void test() {
unique_ptr<MyClass> ptr1(new MyClass("obj1"));
// can use -> (and *) on the unique_ptr just like with a normal pointer
ptr1->methodA();
// to get a plain pointer from the unique_ptr, we use the get() method
someMethod(ptr1.get());
// use std::move to transfer ownership to ptr2 - ptr1 now holds no pointer
unique_ptr<MyClass> ptr2(std::move(ptr1));
// assign a new pointer to ptr1
ptr1.reset(new MyClass("obj2"));
// assign a new pointer to ptr2 - "obj1" will now automatically be deleted
ptr2.reset("obj3");
// set ptr1 to contain nothing - "obj2" will now automatically be deleted
ptr1.reset();
// "obj3" will automatically deleted at the end of this function, as ptr2 goes out of scope
}
The following parts will describe three useful ways in which unique_ptr
is often used …
unique_ptr for class members
A simple use of unique_ptr
is for replacing the use of pointers in storing class members. For example, say you have the following class which simply stores a pointer to an int:
class A {
public:
int* i;
A():i(new int(0)) { }
~A() {
if(i) {
delete i;
}
}
private:
// we need to explicitly disable value-copying, as it's not safe!
A(const A&);
A& operator=(const A&);
};
Using unique_ptr
, this class simplifies to:
class B {
public:
unique_ptr<int> i;
B():i(new int(0)) { }
};
The destructor is no longer needed, and value-copying will automatically be disabled, as unique_ptr
is not copyable. On top of that, this unique_ptr
version also gets a move constructor defined automatically, thanks to unique_ptr
being moveable (whereas the former would have to additionally define a move constructor to get that).
This code is not only more concise, but by eliminating the need to remember boilerplate code, it also eliminates two dangers which are common causes of memory leaks and memory corruption:
- forgetting to delete all class-member pointers in the destructor.
- forgetting to disable value-copying (or to define a suitable copy-constructor + operator= to handle it safely).
Making unique_ptr class members work with Forward Declarations
Say you're declaring ClassA to have a class member variable that is a pointer to ClassB, and you want to store that pointer using a unique_ptr
object. And say you don't want to #include
ClassB.h in ClassA.h (rather you want to forward declare ClassB there in order to save compilation time etc).
When you try to do this, you may sometimes encounter compilation problems, depending upon the compiler. In which case, to fix this you may need to ensure that the constructor and/or destructor are defined in the corresponding .cpp file, rather than being defined in the .h file or being not defined at all and thus left to default implementations.
Note that you would in any case generally have needed to do exactly this before when using a plain pointer and forward declaration. It's only now that using unique_ptr
that you may have been able to avoid writing a destructor and just relied on the default constructor and/or destructor, as in the above example where the destructor was no longer needed).
This same caveat also applies to using shared_ptr
for class member variables, although it again depends on the compiler.
unique_ptr for local variables within functions
A second example of the use of unique_ptr
is for local variables within functions. Say you have the following function:
void methodA() {
int* buf = new int[256];
int result = fillBuf(buf))
if(result == -1) {
return;
}
printf("Result: %d", result);
delete[] buf;
}
The above code has a couple of major safety issues:
- it is forgetting to delete
buf
whenreturn
is called, and thus will leak memory. - if any exception is thrown by
fillBuf(buf)
, it will again fail to delete `buf and thus leak memory.
A safer form of this function while still using a plain pointer would thus be something like:
void methodA() {
int* buf = new int[256];
try {
int result = fillBuf(buf))
if(result == -1) {
delete[] buf;
return;
}
printf("Result: %d", result);
}
catch(...) {
delete[] buf;
throw;
}
delete[] buf;
}
This should now be safe, however it's a lot of code to repeat and get right every time.
With unique_ptr
, you avoid all this boilerplate – you just do the following, and the code is perfectly safe:
void methodA() {
unique_ptr<int> buf(new int[256]);
int result = fillBuf(buf))
if(result == -1) {
return;
}
printf("Result: %d", result);
}
unique_ptr in STL collections
When using STL collections in 'old C++', you have the choice of:
- storing objects by value, which can add a lot of overhead quickly if the objects are large and thus being copied around.
- storing objects with pointers, which suddenly adds a whole lot of memory management issues:
- whenever an item is erased from the collection, the user has to remember to also the object it points to
- when the collection itself is destroyed, the user has to first remember to iterate the entire collection in order to remove+erase any objects it still contains.
C++11 solves this above conundrum with move constructors (see my post on move semantics). These allow any object which defines cheap move constructors to be stored in collections by value rather than pointer, with little/no performance overhead as before (in fact potentially with performance gains due to less indirection).
unique_ptr
also defines such move constructors, thus although it is not copyable, it is moveable, and so the collections still have a way to move it around – transferring the pointer that it points to between unique_ptr
objects.
Thus for objects which don't define inexpensive move constructors, unique_ptr
provides a safe way to store them in STL collections, with no difference in performance from storing plain pointers, i.e.:
std::vector<unique_ptr<MyClass>> v;
v.push_back(unique_ptr<MyClass>("hello world"));
v.emplace_back(new MyClass("hello world"));
MyClass* m = v.get();
This example also demonstrates the use of the new emplace_back
in C++11, reducing a little verbosity otherwise attached with using unique_ptr
to wrap the objects in the vector. emplace_back
works like push_back
, but allows you to simply pass the arguments you would otherwise pass to the constructor for the object which you're storing in the vector, rather than passing the object itself. This method can also be more efficient than using the old push_back
, so should be generally preferred.
shared_ptr
shared_ptr
functions the same way as unique_ptr
– holding a pointer, providing the same basic interface for construction and using the pointer, and ensuring that the pointer is deleted on destruction.
Unlike unique_ptr
, it also allows copying of the shared_ptr
object to another shared_ptr
, and then ensures that the pointer is still guaranteed to always be deleted once (but not before) all shared_ptr
objects that were holding it are destroyed (or have released it).
It does this using reference counting – it keeps a shared count of how many shared_ptr
objects are holding the same pointer. This reference counting is done using atomic functions and is thus threadsafe.
An example of its use is as follows:
struct MyClass {
MyClass(const char* s);
void methodA();
};
void someMethod(MyClass* m);
auto ptr = make_shared<MyClass>("obj1");
ptr->methodA();
someMethod(ptr.get());
shared_ptr<MyClass> anotherPtr = ptr; // now anotherPtr + ptr are both pointing to the "obj1" object
ptr.reset(new MyClass("obj2"); // now ptr switches to pointing to "obj2", but the "obj1"
// object is not deleted as anotherPtr is still holding it
anotherPtr.reset(); // now no shared_ptr object is referencing the "obj1" MyClass*, so it is deleted
// "obj2" will be automically deleted when ptr goes out of scope
Note the use of make_shared
in the above – while it is possible to create a shared_ptr
using the same syntax as for unique_ptr
(passing a pointer to its constructor), make_shared
should be always preferred, as it also happens to be more efficient (only requiring one memory allocation rather than two).
shared_ptr and thread-safety
One place where the use of shared_ptr
can really shine (and the main case where you would need to use shared_ptr
rather than unique_ptr
is in multi-threaded applications. When you're not sure which thread will finish needing an object last, you can simply give each thread a shared_ptr
that references the object.
However, note here that I said that you give each thread one such object. The shared_ptr
class is not thread-safe for the case that two threads try to access the same shared_ptr
object concurrently. Rather, thread-safety is ensured as long as each thread has their own shared_ptr
objects (which may in turn all share+reference the same pointer).
weak_ptr
A weak_ptr
is used to hold a non-owning reference to a pointer that is managed by a shared_ptr
(or multiple shared_ptr
objects). It must be converted to shared_ptr
in order to access the referenced pointer.
weak_ptr
models temporary ownership: when an object needs to be accessed only if it exists, and it may be deleted at any time by someone else, weak_ptr
is used to track the object, and it is converted to std::shared_ptr to assume temporary ownership. If the original shared_ptr
is destroyed at this time, the object's lifetime is extended until the temporary shared_ptr
is destroyed as well.
weak_ptr
is primarily useful in the rare cases where it is needed in order to break 'circular references' – a problem with the use of reference counting in shared_ptr
. An example of this would be a doubly-linked-list containing two nodes – Node A and Node B – here Node A will contain a forward-reference to Node B, and Node B will contain a back-reference to Node A. If these references are shared_ptr
objects, then their reference counts will never reach zero, as even once everything else that refers to Node A and Node B is destroyed, they will still be referencing each other.
auto sp = std::make_shared<string>("hello world");
std::weak_ptr<string> wp = sp;
if(auto spt = wp.lock()) { // to be safe, have to be copy into a shared_ptr before usage
cout << *spt << endl;
}
else {
cout << "sp was deleted already\n";
}
General Memory Management Guidelines – What to use when?
Method 1: just use shared_ptr everywhere
One simple approach is to use shared_ptr
everywhere (together with weak_ptr
in the rare case where you have cycles, but most programmers will never encounter this problem).
This has the advantage of being both safe and simple. And this is precisely the approach that the Objective-C language has taken with it's Automatic Reference Counting – in it's latest iteration it automatically adds reference counting to all objects, making this transparent so that the user is still just allocating and using pointers, but internally these are being reference counted. It's also similar to the approach many other languages take with garbage collection.
The argument against this
There are however are a couple of issues with this approach:
- if your code is being used by other code (i.e. if you're creating a library), then if you're requiring
shared_ptr
pointers to be passed to your functions for example, you're imposing this (your) style of memory management on anybody using your code, which is both not neccessary and not appropriate. shared_ptr
does incur a little overhead in terms of the atomic operations used in the reference counting, and in that it has to allocate a little extra memory to store the reference count (which is only really an issue if having say millions of pointers to very small objects).- by consistently using
unique_ptr
,shared_ptr
and plain-pointer where appropriate (i.e. the following guidelines), the ownership+memory-management+lifetime of objects is clear to anyone looking at code – at a class or method.- i.e. when you use
shared_ptr
everywhere, then looking at a method that takes ashared_ptr
, you have no idea what that method will do with it – whether it will retain it or not. - such complex or unclear ownership models tend to lead to difficult to follow couplings of different parts of the application through shared state that may not be easily trackable.
- i.e. when you use
- C++ gurus also seem to be strongly against this idea, i.e. Bjarne Stroustrop argues that "
shared_ptr
should be used as a last resort when an object's life-time is uncertain" (mainly it appears for the previous reason – making ownership+lifetime of resources clear)
A matter of personal preference perhaps
You might argue that resource lifetime+ownership is all stuff that one ideally shouldn't need to care about, now that you have shared_ptr
and can just use it everywhere. You could also make similar arguments against the Final guideline (see below) of avoiding dynamic memory + pointers whenever possible, arguing that this also requires too much thinking about the performance tradeoffs and understanding of the implementations of the classes you're using – how much data is being copied by the objects' move constructors, and are you sure that they have move constructors defined? And that it's easer to simply not bother with defining move constructors correctly for all your classes, and cleaner and simpler just to use shared_ptr
everywhere.
I tend to agree that simply using shared_ptr
generally is a pretty safe way to do things for many kinds of projects (but I'd be interested to hear comments from those who disagree). I'd say especially for relative beginners, that you could do much worse than to just use shared_ptr
everywhere. Also, basically all the other popular programming languages out there are now happily using either automatic reference counting or garbage collection these days, so using C++ effectively in the same way – by using shared_ptr
everywhere – can't be such a horrible idea.
Using shared_ptr
everywhere will also hurt C++'s performance a little. And I guess people could argue that if you don't care about performance so much, and don't want to think about object lifecycle and such, then there's many other languages out there for you to use (in fact, pretty much every other language out there).
I don't totally buy this argument either though – using shared_ptr
everywhere is really not a significant performance cost most of the time (nowhere near as bad as switching to Ruby for example), and I'd like to see C++ as a good tool for many kinds of programming needs, not only a tool to be used by programmers that like/need to invest extra time in understanding and thinking more about memory managment and object ownership+lifetimes in order to squeeze final couple of % of performance out of their programs.
- note also that you can avoid unnecessary performance penalties from using
shared_ptr
by minimizing copying of theshared_ptr
objects (as that incurs atomic increments and decrements which are the main overhead)
Method 2: some general guidelines
These general guidelines are a little more complicated than above method of just using shared_ptr
. They require some thinking about the concept of 'ownership' of your objects – who is the owner of this object, and is there one owner or many? Generally there should just be one owner, however in some cases ownership will need to be shared (and thus in these cases, but only in these cases,shared_ptr
is to used). Plain pointers, when used, should always be perceived as non-owning pointers that you must guarantee never outlive the object they points to.
The following guidelines apply:
- when an object is allocated, it should generally be immediately assigned to a
shared_ptr
orunique_ptr
that is the 'owner':- typically this
shared_ptr
/unique_ptr
should act as the 'owner' of the object - plain pointers are thus never used to act as the 'owners' of objects
- the exception to this immediate assignment rule is things like factory methods that return a plain pointer to the object they create – in this case however, the callee still should generally be immediately assigning this returned object to a
shared_ptr
/unique_ptr
- also, note here that some argue that even in such cases, the object should still be immediately assigned to a
unique_ptr
, which these methods should return, and the object then gets transferred (moved) from thisunique_ptr
to anotherunique_ptr
/shared_ptr
for ownership – this all being done in order to ensure that the object is at all times 'owned' by a smart pointer so that there is no risk of memory leaks in the case of an exception being thrown etc
- typically this
- methods should take plain-pointers in their arguments, unless they plan to store a reference to a passed object beyond the life of the method (or pass it to another function that does this and is thus also requiring a
shared_ptr
), in which case they should takeshared_ptr
. - methods should always return plain-pointers when it is up to the caller to handle memory management of the object, i.e. to assume ownership of it with a
shared_ptr
/unique_ptr
or to pass to someone else to own it. Examples of such methods would be:- factory methods to create an instance of some class
- as mentioned earlier, some people argue that
unique_ptr
should be used in this case, in order that there is always an owner (in case some exception is thrown at any point and thus there's risk of a memory leak)
- methods that are accessor methods to an owned object that is a
unique_ptr
should return a plain pointer. Examples of such methods would be:- a singleton
getInstance()
method.
- a singleton
- methods that are accessor methods to an owned object that is a
shared_ptr
should generally return ashared_ptr
object (though this should be a rare case I would think). - use
unique_ptr
whenevershared_ptr
is not neccessary, that is, whenever shared-ownership is not neccessary. It should relatively rarely be that case that shared-ownership is really needed, but cases where it really can be needed include- when shared-ownership is required due to sharing an object between multiple threads.
- for modeling parent-child relations using
shared_ptr
together withweak_ptr
Final guideline – simply avoid dynamic memory + pointers whenever possible
With C++11 it is generally preferable to simply avoid dynamic allocation, thus avoiding the consequent need for any corresponding form of memory management.
With the new move constructors in C++11 (see my post on move semantics), most objects' classes should have move constructors defined, and thus objects should generally be moveable cheaply (for example STL classes should now all be), thus such objects:
- can be stored in containers by value without the need to first wrap them in pointers or smart-pointers to avoid costly copies. Thus for example, instead of
vector<unique_ptr<string>>
orvector<shared_ptr<string>>
orvector<string*>
, the simplevector<string>
should generally be preferred now. - can generally passed to functions by value (rather than by reference or as pointers), without any extra cost
- can generally be returned by functions by value (when returning a temporary at least) without any extra cost
Discussion
I would love to hear all opinions about whether there's any good arguement against someone preferring to simply use smart_ptr
everywhere – it is not what I see the experts recommending, and it is not something I use in my work as it generally involves extremely performance critical code, however it would seem like a relatively safe braindead approach, giving the programmer less to worry about for relatively little performance cost.
Also also any discussion about the 'general guidelines' on when to use unique_ptr
vs plain pointer vs shared_ptr
is welcome.
Nice article!
But I think
unique_ptr buf(new int[256]);
should be
unique_ptr buf(new int[256]);
Thanks, a good coverage of this and the other main C11 stuff in one place.
Will be referring back here I am sure.
You post interesting content here. Your website deserves much bigger audience.
It can go viral if you give it initial boost, i
know very useful service that can help you, just type in google: svetsern traffic tips
Having been reading about this subject recently, all experts explain why unique_ptr should be preferred over shared_ptr.
Stroustrup and Sutter both explain in great detail why this is recommended.
/*Thank you I have been avoiding this for some time. I didn’t know enough to really understand the first example as a rough sketch, so I went ahead and made it compile. Hopefully I didn’t undo anything.
*/
#include
#include
#include
class MyClass {
std::string name;
public:
MyClass(std::string name);
void printName();
};
MyClass::MyClass(std::string newName ) { name = newName; }
void MyClass::printName() { std::cout<< "Name: " <<name <<std::endl; }
int main() {
std::unique_ptr ptr1(new MyClass( “obj1” ));
// can use -> (and *) on the unique_ptr just like with a normal pointer
ptr1->printName();
// to get a plain pointer from the unique_ptr, we use the get() method
MyClass *plainPtr;
plainPtr = ptr1.get();
// use std::move to transfer ownership to ptr2 – ptr1 now holds no pointer
std::unique_ptr ptr2(std::move(ptr1));
// assign a new pointer to ptr1
ptr1.reset(new MyClass(“obj2”));
// assign a new pointer to ptr2 – “obj1” will now automatically be deleted
ptr2.reset(new MyClass(“obj3”));
// set ptr1 to contain nothing – “obj2” will now automatically be deleted
ptr1.reset();
// “obj3” will automatically deleted at the end of this function, as ptr2 goes out of scope
return 0;
}