Smart Pointers
Syntax#
std::shared_ptr<ClassType> variableName = std::make_shared<ClassType>(arg1, arg2, ...);
std::shared_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
std::unique_ptr<ClassType> variableName = std::make_unique<ClassType>(arg1, arg2, ...);
// C++14std::unique_ptr<ClassType> variableName (new ClassType(arg1, arg2, ...));
Remarks#
C++ is not a memory-managed language. Dynamically allocated memory (i.e. objects created with new
) will be “leaked” if it is not explicitly deallocated (with delete
). It is the programmer’s responsibility to ensure that the dynamically allocated memory is freed before discarding the last pointer to that object.
Smart pointers can be used to automatically manage the scope of dynamically allocated memory (i.e. when the last pointer reference goes out of scope it is deleted).
Smart pointers are preferred over “raw” pointers in most cases. They make the ownership semantics of dynamically allocated memory explicit, by communicating in their names whether an object is intended to be shared or uniquely owned.
Use #include <memory>
to be able to use smart pointers.
Sharing ownership (std::shared_ptr)
The class template std::shared_ptr
defines a shared pointer that is able to share ownership of an object with other shared pointers. This contrasts to std::unique_ptr
which represents exclusive ownership.
The sharing behavior is implemented through a technique known as reference counting, where the number of shared pointers that point to the object is stored alongside it. When this count reaches zero, either through the destruction or reassignment of the last std::shared_ptr
instance, the object is automatically destroyed.
// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);
To create multiple smart pointers that share the same object, we need to create another shared_ptr
that aliases the first shared pointer. Here are 2 ways of doing it:
std::shared_ptr<Foo> secondShared(firstShared); // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared; // 2nd way: Assigning
Either of the above ways makes secondShared
a shared pointer that shares ownership of our instance of Foo
with firstShared
.
The smart pointer works just like a raw pointer. This means, you can use *
to dereference them. The regular ->
operator works as well:
secondShared->test(); // Calls Foo::test()
Finally, when the last aliased shared_ptr
goes out of scope, the destructor of our Foo
instance is called.
Warning: Constructing a shared_ptr
might throw a bad_alloc
exception when extra data for shared ownership semantics needs to be allocated. If the constructor is passed a regular pointer it assumes to own the object pointed to and calls the deleter if an exception is thrown. This means shared_ptr<T>(new T(args))
will not leak a T
object if allocation of shared_ptr<T>
fails. However, it is advisable to use make_shared<T>(args)
or allocate_shared<T>(alloc, args)
, which enable the implementation to optimize the memory allocation.
Allocating Arrays([]) using shared_ptr
Unfortunately, there is no direct way to allocate Arrays using make_shared<>
.
It is possible to create arrays for shared_ptr<>
using new
and std::default_delete
.
For example, to allocate an array of 10 integers, we can write the code as
shared_ptr<int> sh(new int[10], std::default_delete<int[]>());
Specifying std::default_delete
is mandatory here to make sure that the allocated memory is correctly cleaned up using delete[]
.
If we know the size at compile time, we can do it this way:
template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
std::shared_ptr<T> operator()const{
auto r = std::make_shared<std::array<T,N>>();
if (!r) return {};
return {r.data(), r};
}
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }
then make_shared_array<int[10]>
returns a shared_ptr<int>
pointing to 10 ints all default constructed.
With C++17, shared_ptr
gained special support for array types. It is no longer necessary to specify the array-deleter explicitly, and the shared pointer can be dereferenced using the []
array index operator:
std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;
Shared pointers can point to a sub-object of the object it owns:
struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);
Both p2
and p1
own the object of type Foo
, but p2
points to its int
member x
. This means that if p1
goes out of scope or is reassigned, the underlying Foo
object will still be alive, ensuring that p2
does not dangle.
Important: A shared_ptr
only knows about itself and all other shared_ptr
that were created with the alias constructor. It does not know about any other pointers, including all other shared_ptr
s created with a reference to the same Foo
instance:
Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this
shared1.reset(); // this will delete foo, since shared1
// was the only shared_ptr that owned it
shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
// deleted already!!
Ownership Transfer of shared_ptr
By default, shared_ptr
increments the reference count and doesn’t transfer the ownership. However, it can be made to transfer the ownership using std::move
:
shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1
Sharing with temporary ownership (std::weak_ptr)
Instances of std::weak_ptr
can point to objects owned by instances of std::shared_ptr
while only becoming temporary owners themselves. This means that weak pointers do not alter the object’s reference count and therefore do not prevent an object’s deletion if all of the object’s shared pointers are reassigned or destroyed.
In the following example instances of std::weak_ptr
are used so that the destruction of a tree object is not inhibited:
#include <memory>
#include <vector>
struct TreeNode {
std::weak_ptr<TreeNode> parent;
std::vector< std::shared_ptr<TreeNode> > children;
};
int main() {
// Create a TreeNode to serve as the root/parent.
std::shared_ptr<TreeNode> root(new TreeNode);
// Give the parent 100 child nodes.
for (size_t i = 0; i < 100; ++i) {
std::shared_ptr<TreeNode> child(new TreeNode);
root->children.push_back(child);
child->parent = root;
}
// Reset the root shared pointer, destroying the root object, and
// subsequently its child nodes.
root.reset();
}
As child nodes are added to the root node’s children, their std::weak_ptr
member parent
is set to the root node. The member parent
is declared as a weak pointer as opposed to a shared pointer such that the root node’s reference count is not incremented. When the root node is reset at the end of main()
, the root is destroyed. Since the only remaining std::shared_ptr
references to the child nodes were contained in the root’s collection children
, all child nodes are subsequently destroyed as well.
Due to control block implementation details, shared_ptr allocated memory may not be released until shared_ptr
reference counter and weak_ptr
reference counter both reach zero.
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
{
// std::make_shared is optimized by allocating only once
// while std::shared_ptr<int>(new int(42)) allocates twice.
// Drawback of std::make_shared is that control block is tied to our integer
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// sh memory should be released at this point...
}
// ... but wk is still alive and needs access to control block
}
// now memory is released (sh and wk)
}
Since std::weak_ptr
does not keep its referenced object alive, direct data access through a std::weak_ptr
is not possible. Instead it provides a lock()
member function that attempts to retrieve a std::shared_ptr
to the referenced object:
#include <cassert>
#include <memory>
int main()
{
{
std::weak_ptr<int> wk;
std::shared_ptr<int> sp;
{
std::shared_ptr<int> sh = std::make_shared<int>(42);
wk = sh;
// calling lock will create a shared_ptr to the object referenced by wk
sp = wk.lock();
// sh will be destroyed after this point, but sp is still alive
}
// sp still keeps the data alive.
// At this point we could even call lock() again
// to retrieve another shared_ptr to the same data from wk
assert(*sp == 42);
assert(!wk.expired());
// resetting sp will delete the data,
// as it is currently the last shared_ptr with ownership
sp.reset();
// attempting to lock wk now will return an empty shared_ptr,
// as the data has already been deleted
sp = wk.lock();
assert(!sp);
assert(wk.expired());
}
}
Unique ownership (std::unique_ptr)
A std::unique_ptr
is a class template that manages the lifetime of a dynamically stored object. Unlike for std::shared_ptr
, the dynamic object is owned by only one instance of a std::unique_ptr
at any time,
// Creates a dynamic int with value of 20 owned by a unique pointer
std::unique_ptr<int> ptr = std::make_unique<int>(20);
(Note: std::unique_ptr
is available since C++11 and std::make_unique
since C++14.)
Only the variable ptr
holds a pointer to a dynamically allocated int
. When a unique pointer that owns an object goes out of scope, the owned object is deleted, i.e. its destructor is called if the object is of class type, and the memory for that object is released.
To use std::unique_ptr
and std::make_unique
with array-types, use their array specializations:
// Creates a unique_ptr to an int with value 59
std::unique_ptr<int> ptr = std::make_unique<int>(59);
// Creates a unique_ptr to an array of 15 ints
std::unique_ptr<int[]> ptr = std::make_unique<int[]>(15);
You can access the std::unique_ptr
just like a raw pointer, because it overloads those operators.
You can transfer ownership of the contents of a smart pointer to another pointer by using std::move
, which will cause the original smart pointer to point to nullptr
.
// 1. std::unique_ptr
std::unique_ptr<int> ptr = std::make_unique<int>();
// Change value to 1
*ptr = 1;
// 2. std::unique_ptr (by moving 'ptr' to 'ptr2', 'ptr' doesn't own the object anymore)
std::unique_ptr<int> ptr2 = std::move(ptr);
int a = *ptr2; // 'a' is 1
int b = *ptr; // undefined behavior! 'ptr' is 'nullptr'
// (because of the move command above)
Passing unique_ptr
to functions as parameter:
void foo(std::unique_ptr<int> ptr)
{
// Your code goes here
}
std::unique_ptr<int> ptr = std::make_unique<int>(59);
foo(std::move(ptr))
Returning unique_ptr
from functions. This is the preferred C++11 way of writing factory functions, as it clearly conveys the ownership semantics of the return: the caller owns the resulting unique_ptr
and is responsible for it.
std::unique_ptr<int> foo()
{
std::unique_ptr<int> ptr = std::make_unique<int>(59);
return ptr;
}
std::unique_ptr<int> ptr = foo();
Compare this to:
int* foo_cpp03();
int* p = foo_cpp03(); // do I own p? do I have to delete it at some point?
// it's not readily apparent what the answer is.
The class template make_unique
is provided since C++14. It’s easy to add it manually to C++11 code:
template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(Args&&... args)
{ return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); }
// Use make_unique for arrays
template<typename T>
typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
make_unique(size_t n)
{ return std::unique_ptr<T>(new typename std::remove_extent<T>::type[n]()); }
Unlike the dumb smart pointer (std::auto_ptr
), unique_ptr
can also be instantiated with vector allocation (not std::vector
). Earlier examples were for scalar allocations. For example to have a dynamically allocated integer array for 10 elements, you would specify int[]
as the template type (and not just int
):
std::unique_ptr<int[]> arr_ptr = std::make_unique<int[]>(10);
Which can be simplified with:
auto arr_ptr = std::make_unique<int[]>(10);
Now, you use arr_ptr
as if it is an array:
arr_ptr[2] = 10; // Modify third element
You need not to worry about de-allocation. This template specialized version calls constructors and destructors appropriately. Using vectored version of unique_ptr
or a vector
itself - is a personal choice.
In versions prior to C++11, std::auto_ptr
was available. Unlike unique_ptr
it is allowed to copy auto_ptr
s, upon which the source ptr
will lose the ownership of the contained pointer and the target receives it.
Using custom deleters to create a wrapper to a C interface
Many C interfaces such as SDL2 have their own deletion functions. This means that you cannot use smart pointers directly:
std::unique_ptr<SDL_Surface> a; // won't work, UNSAFE!
Instead, you need to define your own deleter. The examples here use the SDL_Surface
structure which should be freed using the SDL_FreeSurface()
function, but they should be adaptable to many other C interfaces.
The deleter must be callable with a pointer argument, and therefore can be e.g. a simple function pointer:
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);
Any other callable object will work, too, for example a class with an operator()
:
struct SurfaceDeleter {
void operator()(SDL_Surface* surf) {
SDL_FreeSurface(surf);
}
};
std::unique_ptr<SDL_Surface, SurfaceDeleter> a(pointer, SurfaceDeleter{}); // safe
std::unique_ptr<SDL_Surface, SurfaceDeleter> b(pointer); // equivalent to the above
// as the deleter is value-initialized
This not only provides you with safe, zero overhead (if you use unique_ptr
) automatic memory management, you also get exception safety.
Note that the deleter is part of the type for unique_ptr
, and the implementation can use the empty base optimization to avoid any change in size for empty custom deleters. So while std::unique_ptr<SDL_Surface, SurfaceDeleter>
and std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)>
solve the same problem in a similar way, the former type is still only the size of a pointer while the latter type has to hold two pointers: both the SDL_Surface*
and the function pointer! When having free function custom deleters, it is preferable to wrap the function in an empty type.
In cases where reference counting is important, one could use a shared_ptr
instead of an unique_ptr
. The shared_ptr
always stores a deleter, this erases the type of the deleter, which might be useful in APIs. The disadvantages of using shared_ptr
over unique_ptr
include a higher memory cost for storing the deleter and a performance cost for maintaining the reference count.
// deleter required at construction time and is part of the type
std::unique_ptr<SDL_Surface, void(*)(SDL_Surface*)> a(pointer, SDL_FreeSurface);
// deleter is only required at construction time, not part of the type
std::shared_ptr<SDL_Surface> b(pointer, SDL_FreeSurface);
With template auto
, we can make it even easier to wrap our custom deleters:
template <auto DeleteFn>
struct FunctionDeleter {
template <class T>
void operator()(T* ptr) {
DeleteFn(ptr);
}
};
template <class T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;
With which the above example is simply:
unique_ptr_deleter<SDL_Surface, SDL_FreeSurface> c(pointer);
Here, the purpose of auto
is to handle all free functions, whether they return void
(e.g. SDL_FreeSurface
) or not (e.g. fclose
).
Unique ownership without move semantics (auto_ptr)
NOTE: std::auto_ptr
has been deprecated in C++11 and will be removed in C++17. You should only use this if you are forced to use C++03 or earlier and are willing to be careful. It is recommended to move to unique_ptr in combination with std::move
to replace std::auto_ptr
behavior.
Before we had std::unique_ptr
, before we had move semantics, we had std::auto_ptr
. std::auto_ptr
provides unique ownership but transfers ownership upon copy.
As with all smart pointers, std::auto_ptr
automatically cleans up resources (see RAII):
{
std::auto_ptr<int> p(new int(42));
std::cout << *p;
} // p is deleted here, no memory leaked
but allows only one owner:
std::auto_ptr<X> px = ...;
std::auto_ptr<X> py = px;
// px is now empty
This allows to use std::auto_ptr
void f(std::auto_ptr<X> ) {
// assumes ownership of X
// deletes it at end of scope
};
std::auto_ptr<X> px = ...;
f(px); // f acquires ownership of underlying X
// px is now empty
px->foo(); // NPE!
// px.~auto_ptr() does NOT delete
The transfer of ownership happened in the “copy” constructor. auto_ptr
’s copy constructor and copy assignment operator take their operands by non-const
reference so that they could be modified. An example implementation might be:
template <typename T>
class auto_ptr {
T* ptr;
public:
auto_ptr(auto_ptr& rhs)
: ptr(rhs.release())
{ }
auto_ptr& operator=(auto_ptr& rhs) {
reset(rhs.release());
return *this;
}
T* release() {
T* tmp = ptr;
ptr = nullptr;
return tmp;
}
void reset(T* tmp = nullptr) {
if (ptr != tmp) {
delete ptr;
ptr = tmp;
}
}
/* other functions ... */
};
This breaks copy semantics, which require that copying an object leaves you with two equivalent versions of it. For any copyable type, T
, I should be able to write:
T a = ...;
T b(a);
assert(b == a);
But for auto_ptr
, this is not the case. As a result, it is not safe to put auto_ptr
s in containers.
Getting a shared_ptr referring to this
enable_shared_from_this
enables you to get a valid shared_ptr
instance to this
.
By deriving your class from the class template enable_shared_from_this
, you inherit a method shared_from_this
that returns a shared_ptr
instance to this
.
Note that the object must be created as a shared_ptr
in first place:
#include <memory>
class A: public enable_shared_from_this<A> {
};
A* ap1 =new A();
shared_ptr<A> ap2(ap1); // First prepare a shared pointer to the object and hold it!
// Then get a shared pointer to the object from the object itself
shared_ptr<A> ap3 = ap1->shared_from_this();
int c3 =ap3.use_count(); // =2: pointing to the same object
Note(2) you cannot call enable_shared_from_this
inside the constructor.
#include <memory> // enable_shared_from_this
class Widget : public std::enable_shared_from_this< Widget >
{
public:
void DoSomething()
{
std::shared_ptr< Widget > self = shared_from_this();
someEvent -> Register( self );
}
private:
...
};
int main()
{
...
auto w = std::make_shared< Widget >();
w -> DoSomething();
...
}
If you use shared_from_this()
on an object not owned by a shared_ptr
, such as a local automatic object or a global object, then the behavior is undefined. Since C++17 it throws std::bad_alloc
instead.
Using shared_from_this()
from a constructor is equivalent to using it on an object not owned by a shared_ptr
, because the objects is possessed by the shared_ptr
after the constructor returns.
Casting std::shared_ptr pointers
It is not possible to directly use static_cast
, const_cast
, dynamic_cast
and reinterpret_cast
on std::shared_ptr
to retrieve a pointer sharing ownership with the pointer being passed as argument. Instead, the functions std::static_pointer_cast
, std::const_pointer_cast
, std::dynamic_pointer_cast
and std::reinterpret_pointer_cast
should be used:
struct Base { virtual ~Base() noexcept {}; };
struct Derived: Base {};
auto derivedPtr(std::make_shared<Derived>());
auto basePtr(std::static_pointer_cast<Base>(derivedPtr));
auto constBasePtr(std::const_pointer_cast<Base const>(basePtr));
auto constDerivedPtr(std::dynamic_pointer_cast<Derived const>(constBasePtr));
Note that std::reinterpret_pointer_cast
is not available in C++11 and C++14, as it was only proposed by N3920 and adopted into Library Fundamentals TS in February 2014. However, it can be implemented as follows:
template <typename To, typename From>
inline std::shared_ptr<To> reinterpret_pointer_cast(
std::shared_ptr<From> const & ptr) noexcept
{ return std::shared_ptr<To>(ptr, reinterpret_cast<To *>(ptr.get())); }
Writing a smart pointer: value_ptr
A value_ptr
is a smart pointer that behaves like a value. When copied, it copies its contents. When created, it creates its contents.
// Like std::default_delete:
template<class T>
struct default_copier {
// a copier must handle a null T const* in and return null:
T* operator()(T const* tin)const {
if (!tin) return nullptr;
return new T(*tin);
}
void operator()(void* dest, T const* tin)const {
if (!tin) return;
return new(dest) T(*tin);
}
};
// tag class to handle empty case:
struct empty_ptr_t {};
constexpr empty_ptr_t empty_ptr{};
// the value pointer type itself:
template<class T, class Copier=default_copier<T>, class Deleter=std::default_delete<T>,
class Base=std::unique_ptr<T, Deleter>
>
struct value_ptr:Base, private Copier {
using copier_type=Copier;
// also typedefs from unique_ptr
using Base::Base;
value_ptr( T const& t ):
Base( std::make_unique<T>(t) ),
Copier()
{}
value_ptr( T && t ):
Base( std::make_unique<T>(std::move(t)) ),
Copier()
{}
// almost-never-empty:
value_ptr():
Base( std::make_unique<T>() ),
Copier()
{}
value_ptr( empty_ptr_t ) {}
value_ptr( Base b, Copier c={} ):
Base(std::move(b)),
Copier(std::move(c))
{}
Copier const& get_copier() const {
return *this;
}
value_ptr clone() const {
return {
Base(
get_copier()(this->get()),
this->get_deleter()
),
get_copier()
};
}
value_ptr(value_ptr&&)=default;
value_ptr& operator=(value_ptr&&)=default;
value_ptr(value_ptr const& o):value_ptr(o.clone()) {}
value_ptr& operator=(value_ptr const&o) {
if (o && *this) {
// if we are both non-null, assign contents:
**this = *o;
} else {
// otherwise, assign a clone (which could itself be null):
*this = o.clone();
}
return *this;
}
value_ptr& operator=( T const& t ) {
if (*this) {
**this = t;
} else {
*this = value_ptr(t);
}
return *this;
}
value_ptr& operator=( T && t ) {
if (*this) {
**this = std::move(t);
} else {
*this = value_ptr(std::move(t));
}
return *this;
}
T& get() { return **this; }
T const& get() const { return **this; }
T* get_pointer() {
if (!*this) return nullptr;
return std::addressof(get());
}
T const* get_pointer() const {
if (!*this) return nullptr;
return std::addressof(get());
}
// operator-> from unique_ptr
};
template<class T, class...Args>
value_ptr<T> make_value_ptr( Args&&... args ) {
return {std::make_unique<T>(std::forward<Args>(args)...)};
}
This particular value_ptr is only empty if you construct it with empty_ptr_t
or if you move from it. It exposes the fact it is a unique_ptr
, so explicit operator bool() const
works on it. .get()
has been changed to return a reference (as it is almost never empty), and .get_pointer()
returns a pointer instead.
This smart pointer can be useful for pImpl
cases, where we want value-semantics but we also don’t want to expose the contents of the pImpl
outside of the implementation file.
With a non-default Copier
, it can even handle virtual base classes that know how to produce instances of their derived and turn them into value-types.