Smart pointers like such as std::unique_ptr
and std::shared_ptr
encode pointer ownership semantics as part of the type system. They wrap a pointer value, provide pointer-like semantics through operator *()
and operator->()
member functions, and control the lifetime of the pointer they manage. When a smart pointer is constructed from a pointer value, that value is said to be owned by the smart pointer.
Calling std::unique_ptr::release()
will relinquish ownership of the managed pointer value. Destruction of, move assignment of, or calling std::unique_ptr::reset()
on a std::unique_ptr
object will also relinquish ownership of the managed pointer value, but results in destruction of the managed pointer value. If a call to std::shared_ptr::unique()
returns true, then destruction of , or calling std::shared_ptr::reset()
on that std::shared_ptr
object will relinquish ownership of the managed pointer value , but results in destruction of the managed pointer value.
Some smart pointers, such as std::shared_ptr
, allow multiple smart pointer objects to manage the same underlying pointer value. In such cases, the initial smart pointer object owns the pointer value, and subsequent smart pointer objects are related to the original smart pointer. Two smart pointers are related when the initial smart pointer is used in the initialization of the subsequent smart pointer objects. For instance, copying a std::shared_ptr
object to another std::shared_ptr
object via copy assignment creates a relationship between the two smart pointers, while whereas creating a std::shared_ptr
object from the managed pointer value of another std::shared_ptr
object does not.
...
In this noncompliant code example, two unrelated smart pointers are constructed from the same underlying pointer value. When the local, automatic variable p2
is destroyed, it deletes the pointer value it manages. Then, when the local, automatic variable p1
is destroyed, it deletes the same pointer value, resulting in a double-free vulnerability.
Code Block | ||||
---|---|---|---|---|
| ||||
#include <memory> void f() { int *i = new int; std::shared_ptr<int> p1(i); std::shared_ptr<int> p2(i); } |
...
In this compliant solution, the std::shared_ptr
objects are related to one another through copy construction. When the local, automatic variable p2
is destroyed, the use count for the shared pointer value is decremented , but still nonzero. Then, when the local, automatic variable p1
is destroyed, the use count for the shared pointer value is decremented to zero, and the managed pointer is destroyed. This compliant solution also calls std::make_shared
() instead of allocating a raw pointer and storing its value in a local variable.
...
In this noncompliant code example, the B *
pointer value owned by a std::shared_ptr
object is cast to the D *
pointer type with dynamic_cast
in an attempt to obtain a std::shared_ptr
of the polymorphic derived type. However, this eventually results in undefined behavior as the same pointer is stored in two different std::shared_ptr
objects. When g()
exits, the pointer stored in Derived
is freed by the default deleter. Any further use of Poly
results in accessing freed memory. When f()
exits, the same pointer stored in Poly
is destroyed, resulting in a double-free vulnerability.
...
In this compliant solution, the dynamic_cast
is replaced with a call to std::dynamic_pointer_cast()
, which returns a std::shared_ptr
of the polymorphic type with the valid shared pointer value. When g()
exits, the reference count to the underlying pointer is decremented by the destruction of Derived
, but because of the reference held by Poly
(within f()
), the stored pointer value is still valid after g()
returns.
...
The compliant solution is to use std::enable_shared_from_this::shared_from_this()
to get a shared pointer from S
that is related to an existing std::shared_ptr
object. A common implementation strategy is for the std::shared_ptr
constructors to detect the presence of a pointer that inherits from std::enable_shared_from_this
, and automatically update the internal bookkeeping required for for std::enable_shared_from_this::shared_from_this()
to work. Note that std::enable_shared_from_this::shared_from_this()
requires an existing std::shared_ptr
instance that manages the pointer value pointed to by this
. Failure to meet this requirement results in undefined behavior, as it would result in a smart pointer attempting to manage the lifetime of an object that itself does not have lifetime management semantics.
...
[ISO/IEC 14882-2014] | Subclause 20.8, "Smart Pointers" |
...