Introduction
Smart pointers are objects which store pointers to dynamically allocated (heap) objects. They behave much like built-in C++ pointers except that they automatically delete the object pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects. They can also be used to keep track of dynamically allocated objects shared by multiple owners.
Conceptually, smart pointers are seen as owning the object pointed to, and thus responsible for deletion of the object when it is no longer needed. As such, they are examples of the "resource acquisition is initialization" idiom described in Bjarne Stroustrup’s "The C++ Programming Language", 3rd edition, Section 14.4, Resource Management.
This library provides six smart pointer class templates:
-
scoped_ptr
, used to contain ownership of a dynamically allocated object to the current scope; -
scoped_array
, which provides scoped ownership for a dynamically allocated array; -
shared_ptr
, a versatile tool for managing shared ownership of an object or array; -
weak_ptr
, a non-owning observer to ashared_ptr
-managed object that can be promoted temporarily toshared_ptr
; -
intrusive_ptr
, a pointer to objects with an embedded reference count; -
local_shared_ptr
, providing shared ownership within a single thread.
shared_ptr
and weak_ptr
are part of the C++ standard since its 2011 iteration.
In addition, the library contains the following supporting utility functions and classes:
-
make_shared
andallocate_shared
, factory functions for creating objects that return ashared_ptr
; -
make_unique
, a factory function returningstd::unique_ptr
; -
allocate_unique
, a factory function for creating objects using an allocator that returns astd::unique_ptr
; -
enable_shared_from_this
, a helper base class that enables the acquisition of ashared_ptr
pointing tothis
; -
enable_shared_from
, a newer and better replacement forenable_shared_from_this
; -
pointer_to_other
, a helper trait for converting one smart pointer type to another; -
static_pointer_cast
and companions, generic smart pointer casts; -
intrusive_ref_counter
, a helper base class containing a reference count. -
atomic_shared_ptr
, a helper class implementing the interface ofstd::atomic
for a value of typeshared_ptr
.
As a general rule, the destructor or operator delete
for an object managed by pointers in the library
are not allowed to throw exceptions.
Revision History
Changes in 1.87.0
-
C++03 is no longer supported, a C++11 compiler is required. This includes GCC 4.8 or later, and MSVC 14.0 or later.
-
The functionality enabled by the macros
BOOST_SP_ENABLE_DEBUG_HOOKS
,BOOST_SP_USE_STD_ALLOCATOR
,BOOST_SP_USE_QUICK_ALLOCATOR
,BOOST_AC_USE_SPINLOCK
,BOOST_AC_USE_PTHREADS
,BOOST_SP_USE_SPINLOCK
, andBOOST_SP_USE_PTHREADS
has been deprecated and support for it will be removed in a future release.
Changes in 1.79.0
-
Added
get_allocator_pointer
Changes in 1.74.0
-
Added
owner_equals
toshared_ptr
,weak_ptr
,local_shared_ptr
-
Added
owner_hash_value
toshared_ptr
,weak_ptr
-
Added
owner_equal_to
,owner_hash
-
Added
std::hash
specializations forshared_ptr
,local_shared_ptr
-
Added
boost::hash
support to, andstd::hash
,std::equal_to
specializations for,weak_ptr
Changes in 1.72.0
-
Added
allocate_unique
Changes in 1.71.0
-
Added aliasing constructors to
weak_ptr
-
Added
weak_ptr<T>::empty()
-
Added
enable_shared_from
,shared_from
, andweak_from
Changes in 1.65.0
-
Added
atomic_shared_ptr
-
Added
local_shared_ptr
,make_local_shared
scoped_ptr: Scoped Object Ownership
Description
The scoped_ptr
class template stores a pointer to a dynamically allocated object.
(Dynamically allocated objects are allocated with the C++ new
expression.) The
object pointed to is guaranteed to be deleted, either on destruction of the scoped_ptr
,
or via an explicit reset
. See the example.
scoped_ptr
is a simple solution for simple needs. It supplies a basic "resource acquisition
is initialization" facility, without shared-ownership or transfer-of-ownership semantics.
Both its name and enforcement of semantics (by being noncopyable) signal its intent to retain
ownership solely within the current scope. Because it is noncopyable, it is safer than shared_ptr
for pointers which should not be copied.
Because scoped_ptr
is simple, in its usual implementation every operation is as fast as for a
built-in pointer and it has no more space overhead that a built-in pointer.
scoped_ptr
cannot be used in C++ Standard Library containers. Use shared_ptr
or std::unique_ptr
if you need a smart pointer that can.
scoped_ptr
cannot correctly hold a pointer to a dynamically allocated array. See scoped_array
for that usage.
The class template is parameterized on T
, the type of the object pointed to. Destroying T
must not thow exceptions,
and T
must be complete at the point scoped_ptr<T>::~scoped_ptr
is instantiated.
Synopsis
scoped_ptr
is defined in <boost/smart_ptr/scoped_ptr.hpp>
.
namespace boost {
template<class T> class scoped_ptr {
private:
scoped_ptr(scoped_ptr const&);
scoped_ptr& operator=(scoped_ptr const&);
void operator==(scoped_ptr const&) const;
void operator!=(scoped_ptr const&) const;
public:
typedef T element_type;
explicit scoped_ptr(T * p = 0) noexcept;
~scoped_ptr() noexcept;
void reset(T * p = 0) noexcept;
T & operator*() const noexcept;
T * operator->() const noexcept;
T * get() const noexcept;
explicit operator bool() const noexcept;
void swap(scoped_ptr & b) noexcept;
};
template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) noexcept;
template<class T>
bool operator==( scoped_ptr<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator==( std::nullptr_t, scoped_ptr<T> const & p ) noexcept;
template<class T>
bool operator!=( scoped_ptr<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator!=( std::nullptr_t, scoped_ptr<T> const & p ) noexcept;
}
Members
element_type
typedef T element_type;
Provides the type of the stored pointer.
constructor
explicit scoped_ptr(T * p = 0) noexcept;
Constructs a scoped_ptr
, storing a copy of p
, which must have been allocated via a
C++ new
expression or be 0. T
is not required be a complete type.
destructor
~scoped_ptr() noexcept;
Destroys the object pointed to by the stored pointer, if any, as if by using
delete this->get()
. T
must be a complete type.
reset
void reset(T * p = 0) noexcept;
Deletes the object pointed to by the stored pointer and then stores a copy of
p
, which must have been allocated via a C++ new
expression or be 0.
Since the previous object needs to be deleted, T
must be a complete type.
indirection
T & operator*() const noexcept;
Returns a reference to the object pointed to by the stored pointer. Behavior is undefined if the stored pointer is 0.
T * operator->() const noexcept;
Returns the stored pointer. Behavior is undefined if the stored pointer is 0.
get
T * get() const noexcept;
Returns the stored pointer. T
need not be a complete type.
conversions
explicit operator bool () const noexcept; // never throws
Returns get() != 0
.
Note
|
On C++03 compilers, the return value is of an unspecified type. |
swap
void swap(scoped_ptr & b) noexcept;
Exchanges the contents of the two smart pointers. T
need not be a complete type.
Free Functions
swap
template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) noexcept;
Equivalent to a.swap(b)
.
comparisons
template<class T> bool operator==( scoped_ptr<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator==( std::nullptr_t, scoped_ptr<T> const & p ) noexcept;
Returns p.get() == nullptr
.
template<class T> bool operator!=( scoped_ptr<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator!=( std::nullptr_t, scoped_ptr<T> const & p ) noexcept;
Returns p.get() != nullptr
.
Example
Here’s an example that uses scoped_ptr
.
#include <boost/scoped_ptr.hpp>
#include <iostream>
struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } };
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; }
int add_one() { return ++*ptr; }
};
int main()
{
boost::scoped_ptr<Shoe> x(new Shoe);
MyClass my_instance;
std::cout << my_instance.add_one() << '\n';
std::cout << my_instance.add_one() << '\n';
}
The example program produces the beginning of a child’s nursery rhyme:
1
2
Buckle my shoe
Rationale
The primary reason to use scoped_ptr
rather than std::auto_ptr
or std::unique_ptr
is to let readers of your code
know that you intend "resource acquisition is initialization" to be applied only for the current scope, and have no intent to transfer ownership.
A secondary reason to use scoped_ptr
is to prevent a later maintenance programmer from adding a function that transfers
ownership by returning the auto_ptr
, because the maintenance programmer saw auto_ptr
, and assumed ownership could safely be transferred.
Think of bool
vs int
. We all know that under the covers bool
is usually just an int
. Indeed, some argued against including bool in the C++
standard because of that. But by coding bool
rather than int
, you tell your readers what your intent is. Same with scoped_ptr
; by using it you are signaling intent.
It has been suggested that scoped_ptr<T>
is equivalent to std::auto_ptr<T> const
. Ed Brey pointed out, however, that reset
will not work on a std::auto_ptr<T> const
.
Handle/Body Idiom
One common usage of scoped_ptr
is to implement a handle/body (also called pimpl) idiom which avoids exposing the body (implementation) in the header file.
The scoped_ptr_example_test.cpp
sample program includes a header file,
scoped_ptr_example.hpp
, which uses a scoped_ptr<>
to an incomplete type to hide the
implementation. The instantiation of member functions which require a complete type occurs in the scoped_ptr_example.cpp
implementation file.
Frequently Asked Questions
-
Why doesn’t
scoped_ptr
have arelease()
member?When reading source code, it is valuable to be able to draw conclusions about program behavior based on the types being used. If
scoped_ptr
had arelease()
member, it would become possible to transfer ownership of the held pointer, weakening its role as a way of limiting resource lifetime to a given context. Usestd::auto_ptr
where transfer of ownership is required. (supplied by Dave Abrahams)
scoped_array: Scoped Array Ownership
Description
The scoped_array
class template stores a pointer to a dynamically allocated array.
(Dynamically allocated arrays are allocated with the C++ new[]
expression.) The array
pointed to is guaranteed to be deleted, either on destruction of the scoped_array
,
or via an explicit reset
.
The scoped_array
template is a simple solution for simple needs. It supplies a basic
"resource acquisition is initialization" facility, without shared-ownership or
transfer-of-ownership semantics. Both its name and enforcement of semantics
(by being noncopyable) signal its intent to retain ownership solely within the current scope.
Because it is noncopyable, it is safer than shared_ptr<T[]>
for pointers which should not be copied.
Because scoped_array
is so simple, in its usual implementation every operation is as fast as a
built-in array pointer and it has no more space overhead that a built-in array pointer.
It cannot be used in C++ standard library containers. See shared_ptr<T[]>
if scoped_array
does not meet your needs.
It cannot correctly hold a pointer to a single object. See scoped_ptr
for that usage.
std::vector
is an alternative to scoped_array
that is a bit heavier duty but far more flexible.
boost::array
is an alternative that does not use dynamic allocation.
The class template is parameterized on T
, the type of the object pointed to.
Synopsis
scoped_array
is defined in <boost/smart_ptr/scoped_array.hpp>
.
namespace boost {
template<class T> class scoped_array {
private:
scoped_array(scoped_array const &);
scoped_array & operator=(scoped_array const &);
void operator==( scoped_array const& ) const;
void operator!=( scoped_array const& ) const;
public:
typedef T element_type;
explicit scoped_array(T * p = 0) noexcept;
~scoped_array() noexcept;
void reset(T * p = 0) noexcept;
T & operator[](std::ptrdiff_t i) const noexcept;
T * get() const noexcept;
explicit operator bool () const noexcept;
void swap(scoped_array & b) noexcept;
};
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
template<class T>
bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;
template<class T>
bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
}
Members
element_type
typedef T element_type;
Provides the type of the stored pointer.
constructors
explicit scoped_array(T * p = 0) noexcept;
Constructs a scoped_array
, storing a copy of p
, which must have been
allocated via a C++ new[]
expression or be 0. T
is not required be a complete type.
destructor
~scoped_array() noexcept;
Deletes the array pointed to by the stored pointer. Note that delete[]
on a pointer with
a value of 0 is harmless. T
must be complete, and delete[]
on the stored pointer must
not throw exceptions.
reset
void reset(T * p = 0) noexcept;
Deletes the array pointed to by the stored pointer and then stores a copy of p
,
which must have been allocated via a C++ new[]
expression or be 0. T
must be complete,
and delete[]
on the stored pointer must not throw exceptions.
subscripting
T & operator[](std::ptrdiff_t i) const noexcept;
Returns a reference to element i
of the array pointed to by the stored pointer.
Behavior is undefined and almost certainly undesirable if the stored pointer is 0,
or if i
is less than 0 or is greater than or equal to the number of elements in
the array.
get
T * get() const noexcept;
Returns the stored pointer. T
need not be a complete type.
conversions
explicit operator bool () const noexcept;
Returns get() != 0
.
Note
|
On C++03 compilers, the return value is of an unspecified type. |
swap
void swap(scoped_array & b) noexcept;
Exchanges the contents of the two smart pointers. T
need not be a complete type.
Free Functions
swap
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
Equivalent to a.swap(b)
.
comparisons
template<class T> bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;
Returns p.get() == nullptr
.
template<class T> bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
Returns p.get() != nullptr
.
shared_ptr: Shared Ownership
Description
The shared_ptr
class template stores a pointer to a dynamically allocated object, typically with a C++ new
-expression.
The object pointed to is guaranteed to be deleted when the last shared_ptr
pointing to it is destroyed or reset.
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );
shared_ptr
deletes the exact pointer that has been passed at construction time, complete with its original type, regardless
of the template parameter. In the second example above, when p2
is destroyed or reset, it will call delete
on the original
int*
that has been passed to the constructor, even though p2
itself is of type shared_ptr<void>
and stores a pointer of
type void*
.
Every shared_ptr
meets the CopyConstructible
, MoveConstructible
, CopyAssignable
and MoveAssignable
requirements of the
C++ Standard Library, and can be used in standard library containers. Comparison operators are supplied so that shared_ptr
works with the standard library’s associative containers.
Because the implementation uses reference counting, cycles of shared_ptr
instances will not be reclaimed. For example, if main()
holds a shared_ptr
to A
, which directly or indirectly holds a shared_ptr
back to A
, A’s use count will be 2. Destruction
of the original `shared_ptr
will leave A
dangling with a use count of 1. Use weak_ptr
to "break cycles."
The class template is parameterized on T
, the type of the object pointed to. shared_ptr
and most of its member functions place
no requirements on T
; it is allowed to be an incomplete type, or void
. Member functions that do place additional requirements
(constructors, reset
) are explicitly documented below.
shared_ptr<T>
can be implicitly converted to shared_ptr<U>
whenever T*
can be implicitly converted to U*
. In particular,
shared_ptr<T>
is implicitly convertible to shared_ptr<T const>
, to shared_ptr<U>
where U
is an accessible base of T
,
and to shared_ptr<void>
.
shared_ptr
is now part of the C++11 Standard, as std::shared_ptr
.
Starting with Boost release 1.53, shared_ptr
can be used to hold a pointer to a dynamically allocated array. This is accomplished
by using an array type (T[]
or T[N]
) as the template parameter. There is almost no difference between using an unsized array,
T[]
, and a sized array, T[N]
; the latter just enables operator[]
to perform a range check on the index.
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );
Best Practices
A simple guideline that nearly eliminates the possibility of memory leaks is: always use a named smart pointer variable to hold the result
of new
. Every occurence of the new
keyword in the code should have the form:
shared_ptr<T> p(new Y);
It is, of course, acceptable to use another smart pointer in place of shared_ptr
above; having T
and Y
be the same type, or passing
arguments to the constructor of Y
is also OK.
If you observe this guideline, it naturally follows that you will have no explicit delete
statements; try
/catch
constructs will be rare.
Avoid using unnamed shared_ptr
temporaries to save typing; to see why this is dangerous, consider this example:
void f(shared_ptr<int>, int);
int g();
void ok()
{
shared_ptr<int> p( new int(2) );
f( p, g() );
}
void bad()
{
f( shared_ptr<int>( new int(2) ), g() );
}
The function ok
follows the guideline to the letter, whereas bad
constructs the temporary shared_ptr
in place, admitting the possibility of
a memory leak. Since function arguments are evaluated in unspecified order, it is possible for new int(2)
to be evaluated first, g()
second,
and we may never get to the shared_ptr
constructor if g
throws an exception. See Herb Sutter’s treatment of
the issue for more information.
The exception safety problem described above may also be eliminated by using the make_shared
or allocate_shared
factory
functions defined in <boost/smart_ptr/make_shared.hpp>
. These factory functions also provide an efficiency benefit by consolidating allocations.
Synopsis
shared_ptr
is defined in <boost/smart_ptr/shared_ptr.hpp>
.
namespace boost {
class bad_weak_ptr: public std::exception;
template<class T> class weak_ptr;
template<class T> class shared_ptr {
public:
typedef /*see below*/ element_type;
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
~shared_ptr() noexcept;
shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r, element_type * p) noexcept;
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(shared_ptr const & r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;
shared_ptr & operator=(shared_ptr const && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(std::nullptr_t) noexcept;
void reset() noexcept;
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
T & operator*() const noexcept; // only valid when T is not an array type
T * operator->() const noexcept; // only valid when T is not an array type
// only valid when T is an array type
element_type & operator[](std::ptrdiff_t i) const noexcept;
element_type * get() const noexcept;
bool unique() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
void swap(shared_ptr & b) noexcept;
template<class Y> bool owner_before(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_before(weak_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(weak_ptr<Y> const & r) const noexcept;
std::size_t owner_hash_value() const noexcept;
};
template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
template<class T>
typename shared_ptr<T>::element_type *
get_pointer(shared_ptr<T> const & p) noexcept;
template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
template<class D, class T> D * get_deleter(shared_ptr<T> const & p) noexcept;
template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;
template<class T> shared_ptr<T> atomic_load( shared_ptr<T> const * p ) noexcept;
template<class T>
shared_ptr<T> atomic_load_explicit( shared_ptr<T> const * p, int ) noexcept;
template<class T>
void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
void atomic_store_explicit( shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
template<class T>
bool atomic_compare_exchange(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w ) noexcept;
template<class T>
bool atomic_compare_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w, int, int ) noexcept;
}
Members
element_type
typedef ... element_type;
element_type
is T
when T
is not an array type, and U
when T
is U[]
or U[N]
.
default constructor
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
-
- Effects
-
Constructs an empty
shared_ptr
. - Postconditions
-
use_count() == 0 && get() == 0
.
pointer constructor
template<class Y> explicit shared_ptr(Y * p);
-
- Requires
-
Y
must be a complete type. The expressiondelete[] p
, whenT
is an array type, ordelete p
, whenT
is not an array type, must be well-formed, well-defined, and not throw exceptions. WhenT
isU[N]
,Y(*)[N]
must be convertible toT*
; whenT
isU[]
,Y(*)[]
must be convertible toT*
; otherwise,Y*
must be convertible toT*
. - Effects
-
When
T
is not an array type, constructs ashared_ptr
that owns the pointerp
. Otherwise, constructs ashared_ptr
that ownsp
and a deleter of an unspecified type that callsdelete[] p
. - Postconditions
-
use_count() == 1 && get() == p
. IfT
is not an array type andp
is unambiguously convertible toenable_shared_from_this<V>*
for someV
,p->shared_from_this()
returns a copy of*this
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained. - Exception safety
-
If an exception is thrown, the constructor calls
delete[] p
, whenT
is an array type, ordelete p
, whenT
is not an array type.
Note
|
p must be a pointer to an object that was allocated via a C++ new expression or be 0. The postcondition that use count is 1 holds even if p
is 0; invoking delete on a pointer that has a value of 0 is harmless.
|
Note
|
This constructor is a template in order to remember the actual pointer type passed. The destructor will call delete with the same pointer, complete
with its original type, even when T does not have a virtual destructor, or is void .
|
constructors taking a deleter
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
-
- Requires
-
D
must beCopyConstructible
. The copy constructor and destructor ofD
must not throw. The expressiond(p)
must be well-formed, well-defined, and not throw exceptions.A
must be anAllocator
, as described in section Allocator Requirements [allocator.requirements] of the C++ Standard. WhenT
isU[N]
,Y(*)[N]
must be convertible toT*
; whenT
isU[]
,Y(*)[]
must be convertible toT*
; otherwise,Y*
must be convertible toT*
. - Effects
-
Constructs a
shared_ptr
that owns the pointerp
and the deleterd
. The constructors taking an allocator a allocate memory using a copy ofa
. - Postconditions
-
use_count() == 1 && get() == p
. IfT
is not an array type andp
is unambiguously convertible toenable_shared_from_this<V>*
for someV
,p->shared_from_this()
returns a copy of*this
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained. - Exception safety
-
If an exception is thrown,
d(p)
is called.
Note
|
When the the time comes to delete the object pointed to by p , the stored copy of d is invoked with the stored copy of p as an argument.
|
Note
|
Custom deallocators allow a factory function returning a shared_ptr to insulate the user from its memory allocation strategy. Since the deallocator
is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation. For example,
a "no-op" deallocator is useful when returning a shared_ptr to a statically allocated object, and other variations allow a shared_ptr to be used as a wrapper
for another smart pointer, easing interoperability.
|
Note
|
The requirement that the copy constructor of D does not throw comes from the pass by value. If the copy constructor throws, the pointer would leak.
|
copy and converting constructors
shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
If
r
is empty, constructs an emptyshared_ptr
; otherwise, constructs ashared_ptr
that shares ownership withr
. - Postconditions
-
get() == r.get() && use_count() == r.use_count()
.
move constructors
shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
Move-constructs a
shared_ptr
fromr
. - Postconditions
-
*this
contains the old value ofr
.r
is empty andr.get() == 0
.
aliasing constructor
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- Effects
-
Copy-constructs a
shared_ptr
fromr
, while storingp
instead. - Postconditions
-
get() == p && use_count() == r.use_count()
.
aliasing move constructor
template<class Y> shared_ptr(shared_ptr<Y> && r, element_type * p) noexcept;
-
- Effects
-
Move-constructs a
shared_ptr
fromr
, while storingp
instead. - Postconditions
-
get() == p
anduse_count()
equals the old count ofr
.r
is empty andr.get() == 0
.
weak_ptr constructor
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
Constructs a
shared_ptr
that shares ownership withr
and stores a copy of the pointer stored inr
. - Postconditions
-
use_count() == r.use_count()
. - Throws
-
bad_weak_ptr
whenr.use_count() == 0
. - Exception safety
-
If an exception is thrown, the constructor has no effect.
auto_ptr constructors
template<class Y> shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
Constructs a
shared_ptr
, as if by storing a copy ofr.release()
. - Postconditions
-
use_count() == 1
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained. - Exception safety
-
If an exception is thrown, the constructor has no effect.
unique_ptr constructor
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
-
When
r.get() == 0
, equivalent toshared_ptr()
; -
When
D
is not a reference type, equivalent toshared_ptr(r.release(), r.get_deleter())
; -
Otherwise, equivalent to
shared_ptr(r.release(), del)
, wheredel
is a deleter that stores the referencerd
returned fromr.get_deleter()
anddel(p)
callsrd(p)
.
-
- Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained. - Exception safety
-
If an exception is thrown, the constructor has no effect.
destructor
~shared_ptr() noexcept;
-
- Effects
-
-
If
*this
is empty, or shares ownership with anothershared_ptr
instance (use_count() > 1
), there are no side effects. -
Otherwise, if
*this
owns a pointerp
and a deleterd
,d(p)
is called. -
Otherwise,
*this
owns a pointerp
, anddelete p
is called.
-
assignment
shared_ptr & operator=(shared_ptr const & r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
-
- Effects
-
Equivalent to
shared_ptr(r).swap(*this)
. - Returns
-
*this
.
Note
|
The use count updates caused by the temporary object construction and destruction are not considered observable side effects, and the implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary. |
Note
|
In particular, in the example:
both assignments may be no-ops. |
shared_ptr & operator=(shared_ptr && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- Effects
-
Equivalent to
shared_ptr(std::move(r)).swap(*this)
. - Returns
-
*this
.
shared_ptr & operator=(std::nullptr_t) noexcept;
-
- Effects
-
Equivalent to
shared_ptr().swap(*this)
. - Returns
-
*this
.
reset
void reset() noexcept;
-
- Effects
-
Equivalent to
shared_ptr().swap(*this)
.
template<class Y> void reset(Y * p);
-
- Effects
-
Equivalent to
shared_ptr(p).swap(*this)
.
template<class Y, class D> void reset(Y * p, D d);
-
- Effects
-
Equivalent to
shared_ptr(p, d).swap(*this)
.
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- Effects
-
Equivalent to
shared_ptr(p, d, a).swap(*this)
.
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- Effects
-
Equivalent to
shared_ptr(r, p).swap(*this)
.
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
-
- Effects
-
Equivalent to
shared_ptr(std::move(r), p).swap(*this)
.
indirection
T & operator*() const noexcept;
-
- Requires
-
T
should not be an array type. The stored pointer must not be 0. - Returns
-
*get()
.
T * operator->() const noexcept;
-
- Requires
-
T
should not be an array type. The stored pointer must not be 0. - Returns
-
get()
.
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- Requires
-
T
should be an array type. The stored pointer must not be 0.i >= 0
. IfT
isU[N]
,i < N
. - Returns
-
get()[i]
.
get
element_type * get() const noexcept;
-
- Returns
-
The stored pointer.
unique
bool unique() const noexcept;
-
- Returns
-
use_count() == 1
.
use_count
long use_count() const noexcept;
-
- Returns
-
The number of
shared_ptr
objects,*this
included, that share ownership with*this
, or 0 when*this
is empty.
conversions
explicit operator bool() const noexcept;
-
- Returns
-
get() != 0
.
NoteThis conversion operator allows shared_ptr
objects to be used in boolean contexts, likeif(p && p->valid()) {}
.
Note
|
The conversion to bool is not merely syntactic sugar. It allows shared_ptr variables to be declared in conditions when using
dynamic_pointer_cast or weak_ptr::lock .
|
Note
|
On C++03 compilers, the return value is of an unspecified type. |
swap
void swap(shared_ptr & b) noexcept;
-
- Effects
-
Exchanges the contents of the two smart pointers.
owner_before
template<class Y> bool owner_before(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_before(weak_ptr<Y> const & r) const noexcept;
-
- Returns
-
See the description of
operator<
.
owner_equals
template<class Y> bool owner_equals(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(weak_ptr<Y> const & r) const noexcept;
-
- Returns
-
true
if and only if*this
andr
share ownership or are both empty.
owner_hash_value
std::size_t owner_hash_value() const noexcept;
-
- Returns
-
An unspecified hash value such that two instances that share ownership have the same hash value.
Free Functions
comparison
template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- Returns
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- Returns
-
a.get() != b.get()
.
template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
-
- Returns
-
p.get() == 0
.
template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
-
- Returns
-
p.get() != 0
.
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- Returns
-
An unspecified value such that
-
operator<
is a strict weak ordering as described in section [lib.alg.sorting] of the C++ standard; -
under the equivalence relation defined by
operator<
,!(a < b) && !(b < a)
, twoshared_ptr
instances are equivalent if and only if they share ownership or are both empty.
-
Note
|
Allows shared_ptr objects to be used as keys in associative containers.
|
Note
|
The rest of the comparison operators are omitted by design. |
swap
template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
-
- Effects
-
Equivalent to
a.swap(b)
.
get_pointer
template<class T>
typename shared_ptr<T>::element_type *
get_pointer(shared_ptr<T> const & p) noexcept;
-
- Returns
-
p.get()
.
NoteProvided as an aid to generic programming. Used by mem_fn
.
static_pointer_cast
template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
static_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
shared_ptr<T>( r, static_cast<typename shared_ptr<T>::element_type*>(r.get()) )
.
Caution
|
The seemingly equivalent expression shared_ptr<T>(static_cast<T*>(r.get())) will eventually
result in undefined behavior, attempting to delete the same object twice.
|
const_pointer_cast
template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
const_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
shared_ptr<T>( r, const_cast<typename shared_ptr<T>::element_type*>(r.get()) )
.
dynamic_pointer_cast
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
dynamic_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
-
When
dynamic_cast<typename shared_ptr<T>::element_type*>(r.get())
returns a nonzero valuep
,shared_ptr<T>(r, p)
; -
Otherwise,
shared_ptr<T>()
.
-
reinterpret_pointer_cast
template<class T, class U>
shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
reinterpret_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
shared_ptr<T>( r, reinterpret_cast<typename shared_ptr<T>::element_type*>(r.get()) )
.
operator<<
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
-
- Effects
-
os << p.get();
. - Returns
-
os
.
get_deleter
template<class D, class T>
D * get_deleter(shared_ptr<T> const & p) noexcept;
-
- Returns
-
If
*this
owns a deleterd
of type (cv-unqualified)D
, returns&d
; otherwise returns 0.
Atomic Access
Note
|
The function in this section are atomic with respect to the first shared_ptr argument,
identified by *p . Concurrent access to the same shared_ptr instance is not a data race, if
done exclusively by the functions in this section.
|
template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;
-
- Returns
-
false
.
NoteThis implementation is not lock-free.
template<class T> shared_ptr<T> atomic_load( shared_ptr<T> const * p ) noexcept;
template<class T> shared_ptr<T> atomic_load_explicit( shared_ptr<T> const * p, int ) noexcept;
-
- Returns
-
*p
.
NoteThe int
argument is thememory_order
, but this implementation does not use it, as it’s lock-based and therefore always sequentially consistent.
template<class T>
void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
void atomic_store_explicit( shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
-
- Effects
-
p->swap(r)
.
template<class T>
shared_ptr<T> atomic_exchange( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
-
- Effects
-
p->swap(r)
. - Returns
-
The old value of
*p
.
template<class T>
bool atomic_compare_exchange(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w ) noexcept;
template<class T>
bool atomic_compare_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w, int, int ) noexcept;
-
- Effects
-
If
*p
is equivalent to*v
, assignsw
to*p
, otherwise assigns*p
to*v
. - Returns
-
true
if*p
was equivalent to*v
,false
otherwise. - Remarks
-
Two
shared_ptr
instances are equivalent if they store the same pointer value and share ownership.
Example
See shared_ptr_example.cpp for a complete example program. The program builds a
std::vector
and std::set
of shared_ptr
objects.
Note that after the containers have been populated, some of the shared_ptr
objects will have a use count of 1 rather than
a use count of 2, since the set is a std::set
rather than a std::multiset
, and thus does not contain duplicate entries.
Furthermore, the use count may be even higher at various times while push_back
and insert
container operations are performed.
More complicated yet, the container operations may throw exceptions under a variety of circumstances. Getting the memory management
and exception handling in this example right without a smart pointer would be a nightmare.
Handle/Body Idiom
One common usage of shared_ptr
is to implement a handle/body (also called pimpl) idiom which avoids exposing the body (implementation)
in the header file.
The shared_ptr_example2_test.cpp sample program includes a header file,
shared_ptr_example2.hpp, which uses a shared_ptr
to an incomplete type to hide the implementation.
The instantiation of member functions which require a complete type occurs in the shared_ptr_example2.cpp
implementation file. Note that there is no need for an explicit destructor. Unlike ~scoped_ptr
, ~shared_ptr
does not require that T
be a complete type.
Thread Safety
shared_ptr
objects offer the same level of thread safety as built-in types. A shared_ptr
instance can be "read" (accessed using only const operations)
simultaneously by multiple threads. Different shared_ptr
instances can be "written to" (accessed using mutable operations such as operator=
or reset
)
simultaneously by multiple threads (even when these instances are copies, and share the same reference count underneath.)
Any other simultaneous accesses result in undefined behavior.
Examples:
shared_ptr<int> p(new int(42));
shared_ptr
from two threads// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
shared_ptr
instances from two threads// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
shared_ptr
from two threads// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
shared_ptr
from two threads// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
shared_ptr
from two threads// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
Starting with Boost release 1.33.0, shared_ptr
uses a lock-free implementation on most common platforms.
If your program is single-threaded and does not link to any libraries that might have used shared_ptr
in its default configuration,
you can #define
the macro BOOST_SP_DISABLE_THREADS
on a project-wide basis to switch to ordinary non-atomic reference count updates.
(Defining BOOST_SP_DISABLE_THREADS
in some, but not all, translation units is technically a violation of the One Definition Rule and
undefined behavior. Nevertheless, the implementation attempts to do its best to accommodate the request to use non-atomic updates in those
translation units. No guarantees, though.)
You can define the macro BOOST_SP_USE_PTHREADS
to turn off the lock-free platform-specific implementation and fall back to the generic
pthread_mutex_t
-based code.
Frequently Asked Questions
-
There are several variations of shared pointers, with different tradeoffs; why does the smart pointer library supply only a single implementation? It would be useful to be able to experiment with each type so as to find the most suitable for the job at hand?
An important goal of
shared_ptr
is to provide a standard shared-ownership pointer. Having a single pointer type is important for stable library interfaces, since different shared pointers typically cannot interoperate, i.e. a reference counted pointer (used by library A) cannot share ownership with a linked pointer (used by library B.) -
Why doesn’t shared_ptr have template parameters supplying traits or policies to allow extensive user customization?
Parameterization discourages users. The
shared_ptr
template is carefully crafted to meet common needs without extensive parameterization. -
I am not convinced. Default parameters can be used where appropriate to hide the complexity. Again, why not policies?
Template parameters affect the type. See the answer to the first question above.
-
Why doesn’t
shared_ptr
use a linked list implementation?A linked list implementation does not offer enough advantages to offset the added cost of an extra pointer. In addition, it is expensive to make a linked list implementation thread safe.
-
Why doesn’t
shared_ptr
(or any of the other Boost smart pointers) supply an automatic conversion to T*?Automatic conversion is believed to be too error prone.
-
Why does
shared_ptr
supplyuse_count()
?As an aid to writing test cases and debugging displays. One of the progenitors had
use_count()
, and it was useful in tracking down bugs in a complex project that turned out to have cyclic-dependencies. -
Why doesn’t
shared_ptr
specify complexity requirements?Because complexity requirements limit implementors and complicate the specification without apparent benefit to
shared_ptr
users. For example, error-checking implementations might become non-conforming if they had to meet stringent complexity requirements. -
Why doesn’t
shared_ptr
provide arelease()
function?shared_ptr
cannot give away ownership unless it’sunique()
because the other copy will still destroy the object.Consider:
shared_ptr<int> a(new int); shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2 int * p = a.release(); // Who owns p now? b will still call delete on it in its destructor.
Furthermore, the pointer returned by
release()
would be difficult to deallocate reliably, as the sourceshared_ptr
could have been created with a custom deleter, or may have pointed to an object of a different type. -
Why is
operator->()
const, but its return value is a non-const pointer to the element type?Shallow copy pointers, including raw pointers, typically don’t propagate constness. It makes little sense for them to do so, as you can always obtain a non-const pointer from a const one and then proceed to modify the object through it.
shared_ptr
is "as close to raw pointers as possible but no closer".
weak_ptr: Non-owning Observer
Description
The weak_ptr
class template stores a "weak reference" to an object that’s already managed by a shared_ptr
.
To access the object, a weak_ptr
can be converted to a shared_ptr
using the shared_ptr
constructor taking
weak_ptr
, or the weak_ptr
member function lock
. When the last shared_ptr
to the object goes away and the
object is deleted, the attempt to obtain a shared_ptr
from the weak_ptr
instances that refer to the deleted
object will fail: the constructor will throw an exception of type boost::bad_weak_ptr
, and weak_ptr::lock
will
return an empty shared_ptr
.
Every weak_ptr
meets the CopyConstructible
and Assignable
requirements of the C++ Standard Library, and so
can be used in standard library containers. Comparison operators are supplied so that weak_ptr
works with the standard
library’s associative containers.
weak_ptr
operations never throw exceptions.
The class template is parameterized on T
, the type of the object pointed to.
Compared to shared_ptr
, weak_ptr
provides a very limited subset of operations since accessing its stored pointer is
often dangerous in multithreaded programs, and sometimes unsafe even within a single thread (that is, it may invoke undefined
behavior.) Pretend for a moment that weak_ptr
had a get member function that returned a raw pointer, and consider this innocent
piece of code:
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(int * r = q.get())
{
// use *r
}
Imagine that after the if
, but immediately before r
is used, another thread executes the statement p.reset()
. Now r
is a dangling pointer.
The solution to this problem is to create a temporary shared_ptr
from q
:
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(shared_ptr<int> r = q.lock())
{
// use *r
}
Now r
holds a reference to the object that was pointed by q
. Even if p.reset()
is executed in another thread, the object will stay alive until
r
goes out of scope or is reset. By obtaining a shared_ptr
to the object, we have effectively locked it against destruction.
Synopsis
weak_ptr
is defined in <boost/smart_ptr/weak_ptr.hpp>
.
namespace boost {
template<class T> class weak_ptr {
public:
typedef /*see below*/ element_type;
weak_ptr() noexcept;
template<class Y> weak_ptr(shared_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr const & r) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr && r) noexcept;
template<class Y> weak_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> && r, element_type * p) noexcept;
~weak_ptr() noexcept;
weak_ptr & operator=(weak_ptr const & r) noexcept;
weak_ptr & operator=(weak_ptr && r) noexcept;
template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r) noexcept;
template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r) noexcept;
long use_count() const noexcept;
bool expired() const noexcept;
bool empty() const noexcept;
shared_ptr<T> lock() const noexcept;
void reset() noexcept;
void swap(weak_ptr<T> & b) noexcept;
template<class Y> bool owner_before( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_before( shared_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( shared_ptr<Y> const & r ) const noexcept;
std::size_t owner_hash_value() const noexcept;
};
template<class T, class U>
bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
}
Members
element_type
typedef ... element_type;
element_type
is T
when T
is not an array type, and U
when T
is U[]
or U[N]
.
constructors
weak_ptr() noexcept;
-
- Effects
-
Constructs an empty
weak_ptr
. - Postconditions
-
use_count() == 0
.
template<class Y> weak_ptr(shared_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr const & r) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r) noexcept;
-
- Effects
-
If
r
is empty, constructs an emptyweak_ptr
; otherwise, constructs aweak_ptr
that shares ownership withr
as if by storing a copy of the pointer stored inr
. - Postconditions
-
use_count() == r.use_count()
.
weak_ptr(weak_ptr && r) noexcept;
-
- Effects
-
Constructs a
weak_ptr
that has the valuer
held. - Postconditions
-
r
is empty.
aliasing constructors
template<class Y> weak_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> && r, element_type * p) noexcept;
- Effects
-
Constructs a
weak_ptr
fromr
as if by using the corresponding converting/copy/move constructor, but storesp
instead. - Postconditions
-
use_count() == r.use_count()
. When!expired()
,shared_ptr<T>(*this).get() == p
.
Note
|
These constructors are an extension, not present in std::weak_ptr .
|
destructor
~weak_ptr() noexcept;
-
- Effects
-
Destroys this
weak_ptr
but has no effect on the object its stored pointer points to.
assignment
weak_ptr & operator=(weak_ptr const & r) noexcept;
weak_ptr & operator=(weak_ptr && r) noexcept;
template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r) noexcept;
template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r) noexcept;
-
- Effects
-
Equivalent to
weak_ptr(r).swap(*this)
.
NoteThe implementation is free to meet the effects (and the implied guarantees) via different means, without creating a temporary.
use_count
long use_count() const noexcept;
-
- Returns
-
0 if
*this
is empty; otherwise, the number ofshared_ptr
objects that share ownership with*this
.
expired
bool expired() const noexcept;
-
- Returns
-
use_count() == 0
.
empty
bool empty() const noexcept;
-
- Returns
-
true
when*this
is empty,false
otherwise.
NoteThis function is an extension, not present in std::weak_ptr
.
lock
shared_ptr<T> lock() const noexcept;
-
- Returns
-
expired()? shared_ptr<T>(): shared_ptr<T>(*this)
.
reset
void reset() noexcept;
-
- Effects
-
Equivalent to
weak_ptr().swap(*this)
.
swap
void swap(weak_ptr & b) noexcept;
-
- Effects
-
Exchanges the contents of the two smart pointers.
owner_before
template<class Y> bool owner_before( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_before( shared_ptr<Y> const & r ) const noexcept;
-
- Returns
-
See the description of
operator<
.
owner_equals
template<class Y> bool owner_equals( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( shared_ptr<Y> const & r ) const noexcept;
-
- Returns
-
true
if and only if*this
andr
share ownership or are both empty.
owner_hash_value
std::size_t owner_hash_value() const noexcept;
-
- Returns
-
An unspecified hash value such that two instances that share ownership have the same hash value.
Free Functions
comparison
template<class T, class U>
bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
-
- Returns
-
An unspecified value such that
-
operator<
is a strict weak ordering as described in section [lib.alg.sorting] of the C++ standard; -
under the equivalence relation defined by
operator<
,!(a < b) && !(b < a)
, twoweak_ptr
instances are equivalent if and only if they share ownership or are both empty.
-
Note
|
Allows weak_ptr objects to be used as keys in associative containers.
|
swap
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
-
- Effects
-
Equivalent to
a.swap(b)
.
Frequently Asked Questions
-
Can an object create a weak_ptr to itself in its constructor?
No. A
weak_ptr
can only be created from ashared_ptr
, and at object construction time noshared_ptr
to the object exists yet. Even if you could create a temporaryshared_ptr
tothis
, it would go out of scope at the end of the constructor, and allweak_ptr
instances would instantly expire.The solution is to make the constructor private, and supply a factory function that returns a
shared_ptr
:class X { private: X(); public: static shared_ptr<X> create() { shared_ptr<X> px(new X); // create weak pointers from px here return px; } };
make_shared: Creating shared_ptr
Description
The function templates make_shared
and allocate_shared
provide convenient,
safe and efficient ways to create shared_ptr
objects.
Rationale
Consistent use of shared_ptr
can eliminate the need to use an explicit
delete
, but alone it provides no support in avoiding explicit new
. There
were repeated requests from users for a factory function that creates an
object of a given type and returns a shared_ptr
to it. Besides convenience
and style, such a function is also exception safe and considerably faster
because it can use a single allocation for both the object and its
corresponding control block, eliminating a significant portion of
shared_ptr
construction overhead. This eliminates one of the major
efficiency complaints about shared_ptr
.
The family of overloaded function templates, make_shared
and
allocate_shared
, were provided to address this need. make_shared
uses the
global operator new
to allocate memory, whereas allocate_shared
uses an
user-supplied allocator, allowing finer control.
The rationale for choosing the name make_shared
is that the expression
make_shared<Widget>()
can be read aloud and conveys the intended meaning.
Originally the Boost function templates allocate_shared
and make_shared
were provided for scalar objects only. There was a need to have efficient
allocation of array objects. One criticism of class template shared_array
was always the lack of a utility like make_shared
that uses only a single
allocation. When shared_ptr
was enhanced to support array types, additional
overloads of allocate_shared
and make_shared
were provided for array
types.
Synopsis
make_shared
and allocate_shared
are defined in
<boost/smart_ptr/make_shared.hpp>
.
namespace boost {
template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);// T is not an array
// T is an array of unknown bounds template<class T> shared_ptr<T> make_shared(std::size_t n); template<class T, class A> shared_ptr<T> allocate_shared(const A& a, std::size_t n);
// T is an array of known bounds template<class T> shared_ptr<T> make_shared(); template<class T, class A> shared_ptr<T> allocate_shared(const A& a);
// T is an array of unknown bounds template<class T> shared_ptr<T> make_shared(std::size_t n, const remove_extent_t<T>& v); template<class T, class A> shared_ptr<T> allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
// T is an array of known bounds template<class T> shared_ptr<T> make_shared(const remove_extent_t<T>& v); template<class T, class A> shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);
// T is not an array of unknown bounds template<class T> shared_ptr<T> make_shared_noinit(); template<class T, class A> shared_ptr<T> allocate_shared_noinit(const A& a);
// T is an array of unknown bounds template<class T> shared_ptr<T> make_shared_noinit(std::size_t n); template<class T, class A> shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n); }
Common Requirements
The common requirements that apply to all make_shared
and allocate_shared
overloads, unless specified otherwise, are described below.
- Requires
-
A
shall be an allocator. The copy constructor and destructor ofA
shall not throw exceptions. - Effects
-
Allocates memory for an object of type
T
orn
objects ofU
(ifT
is an array type of the formU[]
andn
is determined by arguments, as specified by the concrete overload). The object is initialized from arguments as specified by the concrete overload. Uses a rebound copy ofa
(for an unspecifiedvalue_type
) to allocate memory. If an exception is thrown, the functions have no effect. - Returns
-
A
shared_ptr
instance that stores and owns the address of the newly constructed object. - Postconditions
-
r.get() != 0
andr.use_count() == 1
, wherer
is the return value. - Throws
-
std::bad_alloc
, an exception thrown fromA::allocate
, or from the initialization of the object. - Remarks
-
-
Performs no more than one memory allocation. This provides efficiency equivalent to an intrusive smart pointer.
-
When an object of an array type is specified to be initialized to a value of the same type
v
, this shall be interpreted to mean that each array element of the object is initialized to the corresponding element fromv
. -
When an object of an array type is specified to be value-initialized, this shall be interpreted to mean that each array element of the object is value-initialized.
-
When a (sub)object of non-array type
U
is specified to be initialized to a valuev
, or constructed fromargs...
,make_shared
shall perform this initialization via the expression::new(p) U(expr)
(whereexpr
isv
orstd::forward<Args>(args)...)
respectively) andp
has typevoid*
and points to storage suitable to hold an object of typeU
. -
When a (sub)object of non-array type
U
is specified to be initialized to a valuev
, or constructed fromargs...
,allocate_shared
shall perform this initialization via the expressionstd::allocator_traits<A2>::construct(a2, p, expr)
(whereexpr
isv
orstd::forward<Args>(args)...)
respectively),p
points to storage suitable to hold an object of typeU
, anda2
of typeA2
is a potentially rebound copy ofa
. -
When a (sub)object of non-array type
U
is specified to be default-initialized,make_shared_noinit
andallocate_shared_noinit
shall perform this initialization via the expression::new(p) U
, wherep
has typevoid*
and points to storage suitable to hold an object of typeU
. -
When a (sub)object of non-array type
U
is specified to be value-initialized,make_shared
shall perform this initialization via the expression::new(p) U()
, wherep
has typevoid*
and points to storage suitable to hold an object of typeU
. -
When a (sub)object of non-array type
U
is specified to be value-initialized,allocate_shared
shall perform this initialization via the expressionstd::allocator_traits<A2>::construct(a2, p)
, wherep
points to storage suitable to hold an object of typeU
anda2
of typeA2
is a potentially rebound copy ofa
. -
Array elements are initialized in ascending order of their addresses.
-
When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements should be destroyed in the reverse order of their construction.
-
Note
|
These functions will typically allocate more memory than the total size of the element objects to allow for internal bookkeeping structures such as the reference counts. |
Free Functions
template<class T, class... Args>
shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
shared_ptr<T> allocate_shared(const A& a, Args&&... args);
-
- Constraints
-
T
is not an array. - Returns
-
A
shared_ptr
to an object of typeT
, constructed fromargs...
. - Examples
-
auto p = make_shared<int>();
-
auto p = make_shared<std::vector<int> >(16, 1);
template<class T>
shared_ptr<T> make_shared(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, std::size_t n);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
A
shared_ptr
to a sequence ofn
value-initialized objects of typeremove_extent_t<T>
. - Examples
-
auto p = make_shared<double[]>(1024);
-
auto p = make_shared<double[][2][2]>(6);
template<class T>
shared_ptr<T> make_shared();
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a);
-
- Constraints
-
T
is an array of known bounds. - Returns
-
A
shared_ptr
to a sequence ofextent_v<T>
value-initialized objects of typeremove_extent_t<T>
. - Examples
-
auto p = make_shared<double[1024]>();
-
auto p = make_shared<double[6][2][2]>();
template<class T> shared_ptr<T>
make_shared(std::size_t n, const remove_extent_t<T>& v);
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
A
shared_ptr
to a sequence ofn
objects of typeremove_extent_t<T>
, each initialized tov
. - Examples
-
auto p = make_shared<double[]>(1024, 1.0);
-
auto p = make_shared<double[][2]>(6, {1.0, 0.0});
-
auto p = make_shared<std::vector<int>[]>(4, {1, 2});
template<class T>
shared_ptr<T> make_shared(const remove_extent_t<T>& v);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);
-
- Constraints
-
T
is an array of known bounds. - Returns
-
A
shared_ptr
to a sequence ofextent_v<T>
objects of typeremove_extent_t<T>
, each initialized tov
. - Examples
-
auto p = make_shared<double[1024]>(1.0);
-
auto p = make_shared<double[6][2]>({1.0, 0.0});
-
auto p = make_shared<std::vector<int>[4]>({1, 2});
template<class T>
shared_ptr<T> make_shared_noinit();
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a);
-
- Constraints
-
T
is not an array, or is an array of known bounds. - Returns
-
A
shared_ptr
to a default-initialized object of typeT
, or a sequence ofextent_v<T>
default-initialized objects of typeremove_extent_t<T>
, respectively. - Example
-
auto p = make_shared_noinit<double[1024]>();
template<class T>
shared_ptr<T> make_shared_noinit(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
A
shared_ptr
to a sequence ofn
default-initialized objects of typeremove_extent_t<T>
. - Example
-
auto p = make_shared_noinit<double[]>(1024);
enable_shared_from_this
Description
The class template enable_shared_from_this
is used as a base class that allows
a shared_ptr
or a weak_ptr
to the current object to be obtained from within a
member function.
enable_shared_from_this<T>
defines two member functions called shared_from_this
that return a shared_ptr<T>
and shared_ptr<T const>
, depending on constness, to
this
. It also defines two member functions called weak_from_this
that return a
corresponding weak_ptr
.
Example
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>
class Y: public boost::enable_shared_from_this<Y>
{
public:
boost::shared_ptr<Y> f()
{
return shared_from_this();
}
};
int main()
{
boost::shared_ptr<Y> p(new Y);
boost::shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
Synopsis
enable_shared_from_this
is defined in <boost/smart_ptr/enable_shared_from_this.hpp>
.
namespace boost {
template<class T> class enable_shared_from_this {
private:
// exposition only
weak_ptr<T> weak_this_;
protected:
enable_shared_from_this() = default;
~enable_shared_from_this() = default;
enable_shared_from_this(const enable_shared_from_this&) noexcept;
enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept;
public:
shared_ptr<T> shared_from_this();
shared_ptr<T const> shared_from_this() const;
weak_ptr<T> weak_from_this() noexcept;
weak_ptr<T const> weak_from_this() const noexcept;
}
}
Members
enable_shared_from_this(enable_shared_from_this const &) noexcept;
-
- Effects
-
Default-constructs
weak_this_
.
Noteweak_this_
is not copied from the argument.
enable_shared_from_this& operator=(enable_shared_from_this const &) noexcept;
-
- Returns
-
*this
.
Noteweak_this_
is unchanged.
template<class T> shared_ptr<T> shared_from_this();
template<class T> shared_ptr<T const> shared_from_this() const;
-
- Returns
-
shared_ptr<T>(weak_this_)
.
NoteThese members throw bad_weak_ptr
when*this
is not owned by ashared_ptr
.
Note
|
the construction of |
template<class T> weak_ptr<T> weak_from_this() noexcept;
template<class T> weak_ptr<T const> weak_from_this() const noexcept;
-
- Returns
-
weak_this_
.
NoteUnlike shared_from_this()
,weak_from_this()
is valid in a destructor and returns aweak_ptr
that isexpired()
but still shares ownership with otherweak_ptr
instances (if any) that refer to the object.
enable_shared_from
Description
enable_shared_from
is used as a base class that allows a shared_ptr
or a
weak_ptr
to be obtained given a raw pointer to the object, by using the
functions shared_from
and weak_from
.
enable_shared_from
differs from enable_shared_from_this<T>
by the fact
that it’s not a template, and is its recommended replacement for new code.
Example
#include <boost/smart_ptr/enable_shared_from.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>
class Y: public boost::enable_shared_from
{
public:
boost::shared_ptr<Y> f()
{
return boost::shared_from( this );
}
};
int main()
{
boost::shared_ptr<Y> p(new Y);
boost::shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
Synopsis
enable_shared_from
is defined in <boost/smart_ptr/enable_shared_from.hpp>
.
namespace boost {
class enable_shared_from: public enable_shared_from_this<enable_shared_from>
{
};
template<class T> shared_ptr<T> shared_from( T * p );
template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
}
Functions
template<class T> shared_ptr<T> shared_from( T * p );
-
- Returns
-
shared_ptr<T>( p->enable_shared_from::shared_from_this(), p )
.
NoteThrows bad_weak_ptr
whenp
is not owned by ashared_ptr
.
template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
-
- Returns
-
weak_ptr<T>( p->enable_shared_from::weak_from_this(), p )
.
NoteUnlike shared_from(this)
,weak_from(this)
is valid in a destructor and returns aweak_ptr
that isexpired()
but still shares ownership with otherweak_ptr
instances (if any) that refer to the object.
make_unique: Creating unique_ptr
Description
The make_unique
function templates provide convenient and safe ways to
create std::unique_ptr
objects.
Rationale
The C++11 standard introduced std::unique_ptr
but did not provide any
make_unique
utility like std::make_shared
that provided the same
exception safety and facility to avoid writing new
expressions. Before it
was implemented by some standard library vendors (and prior to the C++14
standard introducing std::make_unique
), this library provided it due to
requests from users.
This library also provides additional overloads of make_unique
for
default-initialization, when users do not need or want to incur the expense
of value-initialization. The C++20 standard now provides this feature with
std::make_unique_for_overwrite
.
Synopsis
make_unique
is defined in <boost/smart_ptr/make_unique.hpp>
.
namespace boost {
template<class T, class... Args> std::unique_ptr<T> make_unique(Args&&... args);// T is not an array
// T is not an array template<class T> std::unique_ptr<T> make_unique(type_identity_t<T>&& v);
// T is an array of unknown bounds template<class T> std::unique_ptr<T> make_unique(std::size_t n);
// T is not an array template<class T> std::unique_ptr<T> make_unique_noinit();
// T is an array of unknown bounds template<class T> std::unique_ptr<T> make_unique_noinit(std::size_t n); }
Free Functions
template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);
-
- Constraints
-
T
is not an array. - Returns
-
std::unique_ptr<T>(new T(std::forward<Args>(args)...)
. - Example
-
auto p = make_unique<int>();
template<class T>
std::unique_ptr<T> make_unique(type_identity_t<T>&& v);
-
- Constraints
-
T
is not an array. - Returns
-
std::unique_ptr<T>(new T(std::move(v))
. - Example
-
auto p = make_unique<std::vector<int> >({1, 2});
template<class T>
std::unique_ptr<T> make_unique(std::size_t n);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
std::unique_ptr<T>(new remove_extent_t<T>[n]())
. - Example
-
auto p = make_unique<double[]>(1024);
template<class T>
std::unique_ptr<T> make_unique_noinit();
-
- Constraints
-
T
is not an array. - Returns
-
std::unique_ptr<T>(new T)
. - Example
-
auto p = make_unique_noinit<std::array<double, 1024> >();
template<class T>
std::unique_ptr<T> make_unique_noinit(std::size_t n);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
std::unique_ptr<T>(new remove_extent_t<T>[n])
. - Example
-
auto p = make_unique_noinit<double[]>(1024);
allocate_unique: Creating unique_ptr
Description
The allocate_unique
family of function templates provide convenient and safe
ways to obtain a std::unique_ptr
that manages a new object created using an
allocator.
Rationale
The C++14 standard introduced std::make_unique
which used operator new
to
create new objects. However, there is no convenient facility in the standard
library to use an allocator for the creation of the objects managed by
std::unique_ptr
. Users writing allocator aware code have often requested an
allocate_unique
factory function. This function is to std::unique_ptr
what
std::allocate_shared
is to std::shared_ptr
.
Synopsis
allocate_unique
is defined in <boost/smart_ptr/allocate_unique.hpp>
.
namespace boost { template<class T, class A> class alloc_deleter; template<class T, class A> using alloc_noinit_deleter = alloc_deleter<T, noinit_adaptor<A>>;
template<class T, class A, class... Args> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, Args&&... args);// T is not an array
// T is not an array template<class T, class A> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, type_identity_t<T>&& v);
// T is an array of unknown bounds template<class T, class A> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, std::size_t n);
// T is an array of known bounds template<class T, class A> std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>> allocate_unique(const A& a);
// T is an array of unknown bounds template<class T, class A> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);
// T is an array of known bounds template<class T, class A> std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>> allocate_unique(const A& a, const remove_extent_t<T>& v);
// T is not an array template<class T, class A> std::unique_ptr<T, alloc_noinit_deleter<T, A>> allocate_unique_noinit(const A& a);
// T is an array of unknown bounds template<class T, class A> std::unique_ptr<T, alloc_noinit_deleter<T, A>> allocate_unique_noinit(const A& a, std::size_t n);
// T is an array of known bounds template<class T, class A> std::unique_ptr<remove_extent_t<T>[], alloc_noinit_deleter<T, A>> allocate_unique_noinit(const A& a); template<class T, class U, class A> allocator_pointer_t<allocator_rebind_t<A, remove_cv_t<remove_extent_t<T>>>> get_allocator_pointer(const std::unique_ptr<T, alloc_deleter<U, A>>& p) noexcept; }
Common Requirements
The common requirements that apply to all allocate_unique
and
allocate_unique_noinit
overloads, unless specified otherwise, are described
below.
- Requires
-
A
shall be an allocator. The copy constructor and destructor ofA
shall not throw exceptions. - Effects
-
Allocates memory for an object of type
T
orn
objects ofU
(ifT
is an array type of the formU[]
andn
is determined by arguments, as specified by the concrete overload). The object is initialized from arguments as specified by the concrete overload. Uses a rebound copy ofa
(for an unspecifiedvalue_type
) to allocate memory. If an exception is thrown, the functions have no effect. - Returns
-
A
std::unique_ptr
instance that stores and owns the address of the newly constructed object. - Postconditions
-
r.get() != 0
, wherer
is the return value. - Throws
-
An exception thrown from
A::allocate
, or from the initialization of the object. - Remarks
-
-
When an object of an array type is specified to be initialized to a value of the same type
v
, this shall be interpreted to mean that each array element of the object is initialized to the corresponding element fromv
. -
When an object of an array type is specified to be value-initialized, this shall be interpreted to mean that each array element of the object is value-initialized.
-
When a (sub)object of non-array type
U
is specified to be initialized to a valuev
, or constructed fromargs...
,allocate_unique
shall perform this initialization via the expressionstd::allocator_traits<A2>::construct(a2, p, expr)
(whereexpr
isv
orstd::forward<Args>(args)...)
respectively),p
points to storage suitable to hold an object of typeU
, anda2
of typeA2
is a potentially rebound copy ofa
. -
When a (sub)object of non-array type
U
is specified to be default-initialized,allocate_unique_noinit
shall perform this initialization via the expression::new(p) U
, wherep
has typevoid*
and points to storage suitable to hold an object of typeU
. -
When a (sub)object of non-array type
U
is specified to be value-initialized,allocate_unique
shall perform this initialization via the expressionstd::allocator_traits<A2>::construct(a2, p)
, wherep
points to storage suitable to hold an object of typeU
anda2
of typeA2
is a potentially rebound copy ofa
. -
Array elements are initialized in ascending order of their addresses.
-
When the lifetime of the object managed by the return value ends, or when the initialization of an array element throws an exception, the initialized elements should be destroyed in the reverse order of their construction.
-
Free Functions
template<class T, class A, class... Args>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, Args&&... args);
-
- Constraints
-
T
is not an array. - Returns
-
A
std::unique_ptr
to an object of typeT
, constructed fromargs...
. - Examples
-
auto p = allocate_unique<int>(a);
-
auto p = allocate_unique<std::vector<int>>(a, 16, 1);
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, type_identity_t<T>&& v);
-
- Constraints
-
T
is not an array. - Returns
-
A
std::unique_ptr
to an object of typeT
, constructed fromv
. - Example
-
auto p = allocate_unique<std::vector<int>>(a, {1, 2});
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, std::size_t n);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
A
std::unique_ptr
to a sequence ofn
value-initialized objects of typeremove_extent_t<T>
. - Examples
-
auto p = allocate_unique<double[]>(a, 1024);
-
auto p = allocate_unique<double[][2][2]>(a, 6);
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
allocate_unique(const A& a);
-
- Constraints
-
T
is an array of known bounds. - Returns
-
A
std::unique_ptr
to a sequence ofextent_v<T>
value-initialized objects of typeremove_extent_t<T>
. - Examples
-
auto p = allocate_unique<double[1024]>(a);
-
auto p = allocate_unique<double[6][2][2]>(a);
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
A
std::unique_ptr
to a sequence ofn
objects of typeremove_extent_t<T>
, each initialized tov
. - Examples
-
auto p = allocate_unique<double[]>(a, 1024, 1.0);
-
auto p = allocate_unique<double[][2]>(a, 6, {1.0, 0.0});
-
auto p = allocate_unique<std::vector<int>[]>(a, 4, {1, 2});
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
allocate_unique(const A& a, const remove_extent_t<T>& v);
-
- Constraints
-
T
is an array of known bounds. - Returns
-
A
std::unique_ptr
to a sequence ofextent_v<T>
objects of typeremove_extent_t<T>
, each initialized tov
. - Examples
-
auto p = allocate_unique<double[1024]>(a, 1.0);
-
auto p = allocate_unique<double[6][2]>(a, {1.0, 0.0});
-
auto p = allocate_unique<std::vector<int>[4]>(a, {1, 2});
template<class T, class A>
std::unique_ptr<T, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a);
-
- Constraints
-
T
is not an array. - Returns
-
A
std::unique_ptr
to a default-initialized object of typeT
. - Example
-
auto p = allocate_unique_noinit<double>(a);
template<class T, class A>
std::unique_ptr<T, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a, std::size_t n);
-
- Constraints
-
T
is an array of unknown bounds. - Returns
-
A
std::unique_ptr
to a sequence ofn
default-initialized objects of typeremove_extent_t<T>
. - Example
-
auto p = allocate_unique_noinit<double[]>(a, 1024);
template<class T, class A>
std::unique_ptr<remove_extent_t<T>, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a);
-
- Constraints
-
T
is an array of known bounds. - Returns
-
A
std::unique_ptr
to a sequence ofextent_v<T>
default-initialized objects of typeremove_extent_t<T>
. - Example
-
auto p = allocate_unique_noinit<double[1024]>(a);
template<class T, class U, class A>
allocator_pointer_t<allocator_rebind_t<A, remove_cv_t<remove_extent_t<T>>>>
get_allocator_pointer(const std::unique_ptr<T,
alloc_deleter<U, A>>& p) noexcept;
-
- Returns
-
The allocator pointer to the allocation.
- Example
-
auto r = boost::get_allocator_ptr(p);
Deleter
Class template alloc_deleter
is the deleter used by the allocate_unique
functions.
Synopsis
template<class T, class A>
class alloc_deleter {
public:
using pointer =
unspecified
;
explicit alloc_deleter(const A& a) noexcept;
void operator()(pointer p);
};
Members
using pointer =
unspecified
;
-
A type that satisfies NullablePointer.
explicit alloc_deleter(const A& a) noexcept;
-
- Effects
-
Initializes the stored allocator from
a
.
void operator()(pointer p);
-
- Effects
-
Destroys the objects and deallocates the storage referenced by
p
, using the stored allocator.
intrusive_ptr: Managing Objects with Embedded Counts
Description
The intrusive_ptr
class template stores a pointer to an object with an embedded reference count.
Every new intrusive_ptr
instance increments the reference count by using an unqualified call to the
function intrusive_ptr_add_ref
, passing it the pointer as an argument. Similarly, when an intrusive_ptr
is destroyed, it calls intrusive_ptr_release
; this function is responsible for destroying the object when
its reference count drops to zero. The user is expected to provide suitable definitions of these two functions.
On compilers that support argument-dependent lookup, intrusive_ptr_add_ref
and intrusive_ptr_release
should
be defined in the namespace that corresponds to their parameter; otherwise, the definitions need to go in namespace
boost
. The library provides a helper base class template intrusive_ref_counter
which
may help adding support for intrusive_ptr
to user types.
The class template is parameterized on T
, the type of the object pointed to. intrusive_ptr<T>
can be implicitly
converted to intrusive_ptr<U>
whenever T*
can be implicitly converted to U*
.
The main reasons to use intrusive_ptr
are:
-
Some existing frameworks or OSes provide objects with embedded reference counts;
-
The memory footprint of
intrusive_ptr
is the same as the corresponding raw pointer; -
intrusive_ptr<T>
can be constructed from an arbitrary raw pointer of typeT*
.
As a general rule, if it isn’t obvious whether intrusive_ptr
better fits your needs than shared_ptr
, try a shared_ptr
-based design first.
Synopsis
intrusive_ptr
is defined in <boost/smart_ptr/intrusive_ptr.hpp>
.
namespace boost {
template<class T> class intrusive_ptr {
public:
typedef T element_type;
intrusive_ptr() noexcept;
intrusive_ptr(T * p, bool add_ref = true);
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
~intrusive_ptr();
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
void reset();
void reset(T * r);
void reset(T * r, bool add_ref);
T & operator*() const noexcept;
T * operator->() const noexcept;
T * get() const noexcept;
T * detach() noexcept;
explicit operator bool () const noexcept;
void swap(intrusive_ptr & b) noexcept;
};
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
template<class T>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
intrusive_ptr<Y> const & p);
}
Members
element_type
typedef T element_type;
Provides the type of the template parameter T.
constructors
intrusive_ptr() noexcept;
-
- Postconditions
-
get() == 0
.
intrusive_ptr(T * p, bool add_ref = true);
-
- Effects
-
if(p != 0 && add_ref) intrusive_ptr_add_ref(p);
. - Postconditions
-
get() == p
.
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
-
- Effects
-
T * p = r.get(); if(p != 0) intrusive_ptr_add_ref(p);
. - Postconditions
-
get() == r.get()
.
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
-
- Postconditions
-
get()
equals the old value ofr.get()
.r.get() == 0
.
destructor
~intrusive_ptr();
-
- Effects
-
if(get() != 0) intrusive_ptr_release(get());
.
assignment
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
-
- Effects
-
Equivalent to
intrusive_ptr(r).swap(*this)
. - Returns
-
*this
.
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
-
- Effects
-
Equivalent to
intrusive_ptr(std::move(r)).swap(*this)
. - Returns
-
*this
.
reset
void reset();
-
- Effects
-
Equivalent to
intrusive_ptr().swap(*this)
.
void reset(T * r);
-
- Effects
-
Equivalent to
intrusive_ptr(r).swap(*this)
.
void reset(T * r, bool add_ref);
-
- Effects
-
Equivalent to
intrusive_ptr(r, add_ref).swap(*this)
.
indirection
T & operator*() const noexcept;
-
- Requirements
-
get() != 0
. - Returns
-
*get()
.
T * operator->() const noexcept;
-
- Requirements
-
get() != 0
. - Returns
-
get()
.
get
T * get() const noexcept;
-
- Returns
-
the stored pointer.
detach
T * detach() noexcept;
-
- Returns
-
the stored pointer.
- Postconditions
-
get() == 0
.
Note
|
The returned pointer has an elevated reference count. This allows conversion of an intrusive_ptr
back to a raw pointer, without the performance overhead of acquiring and dropping an extra reference.
It can be viewed as the complement of the non-reference-incrementing constructor.
|
Caution
|
Using detach escapes the safety of automatic reference counting provided by intrusive_ptr .
It should by used only where strictly necessary (such as when interfacing to an existing API), and when
the implications are thoroughly understood.
|
conversions
explicit operator bool () const noexcept;
-
- Returns
-
get() != 0
.
NoteThis conversion operator allows intrusive_ptr
objects to be used in boolean contexts, likeif (p && p->valid()) {}
.
Note
|
On C++03 compilers, the return value is of an unspecified type. |
swap
void swap(intrusive_ptr & b) noexcept;
-
- Effects
-
Exchanges the contents of the two smart pointers.
Free Functions
comparison
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
-
- Returns
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
-
- Returns
-
a.get() != b.get()
.
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
-
- Returns
-
a.get() == b
.
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
-
- Returns
-
a.get() != b
.
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
-
- Returns
-
a == b.get()
.
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
-
- Returns
-
a != b.get()
.
template<class T>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
-
- Returns
-
std::less<T *>()(a.get(), b.get())
.
NoteAllows intrusive_ptr
objects to be used as keys in associative containers.
swap
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
-
- Effects
-
Equivalent to
a.swap(b)
.
get_pointer
template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
-
- Returns
-
p.get()
.
NoteProvided as an aid to generic programming. Used by mem_fn
.
static_pointer_cast
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- Returns
-
intrusive_ptr<T>(static_cast<T*>(r.get()))
.
const_pointer_cast
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- Returns
-
intrusive_ptr<T>(const_cast<T*>(r.get()))
.
dynamic_pointer_cast
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- Returns
-
intrusive_ptr<T>(dynamic_cast<T*>(r.get()))
.
operator<<
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
intrusive_ptr<Y> const & p);
-
- Effects
-
os << p.get();
. - Returns
-
os
.
intrusive_ref_counter
Description
The intrusive_ref_counter
class template implements a reference counter for
a derived user’s class that is intended to be used with intrusive_ptr
. The
base class has associated intrusive_ptr_add_ref
and intrusive_ptr_release
functions which modify the reference counter as needed and destroy the user’s
object when the counter drops to zero.
The class template is parameterized on Derived
and CounterPolicy
parameters. The first parameter is the user’s class that derives from
intrusive_ref_counter
. This type is needed in order to destroy the object
correctly when there are no references to it left.
The second parameter is a policy that defines the nature of the reference
counter. The library provides two such policies: thread_unsafe_counter
and
thread_safe_counter
. The former instructs the intrusive_ref_counter
base
class to use a counter only suitable for a single-threaded use. Pointers to a
single object that uses this kind of reference counter must not be used in
different threads. The latter policy makes the reference counter thread-safe,
unless the target platform doesn’t support threading. Since in modern systems
support for threading is common, the default counter policy is
thread_safe_counter
.
Synopsis
intrusive_ref_counter
is defined in
<boost/smart_ptr/intrusive_ref_counter.hpp>
.
namespace boost {
struct thread_unsafe_counter;
struct thread_safe_counter;
template<class Derived, class CounterPolicy = thread_safe_counter>
class intrusive_ref_counter {
public:
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter& v) noexcept;
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
unsigned int use_count() const noexcept;
protected:
~intrusive_ref_counter() = default;
};
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
}
Members
Constructors
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter&) noexcept;
-
- Postconditions
-
use_count() == 0
.
NoteThe pointer to the constructed object is expected to be passed to intrusive_ptr
constructor, assignment operator orreset
method, which would increment the reference counter.
Destructor
~intrusive_ref_counter();
-
- Effects
-
Destroys the counter object.
NoteThe destructor is protected so that the object can only be destroyed through the Derived
class.
Assignment
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
-
- Effects
-
Does nothing, reference counter is not modified.
use_count
unsigned int use_count() const noexcept;
-
- Returns
-
The current value of the reference counter.
NoteThe returned value may not be actual in multi-threaded applications.
Free Functions
intrusive_ptr_add_ref
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- Effects
-
Increments the reference counter.
intrusive_ptr_release
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- Effects
-
Decrements the reference counter. If the reference counter reaches 0, calls
delete static_cast<const Derived*>(p)
.
local_shared_ptr: Shared Ownership within a Single Thread
Description
local_shared_ptr
is nearly identical to shared_ptr
, with the only difference of note being that its reference count is
updated with non-atomic operations. As such, a local_shared_ptr
and all its copies must reside in (be local to) a single
thread (hence the name.)
local_shared_ptr
can be converted to shared_ptr
and vice versa. Creating a local_shared_ptr
from a shared_ptr
creates
a new local reference count; this means that two local_shared_ptr
instances, both created from the same shared_ptr
, refer
to the same object but don’t share the same count, and as such, can safely be used by two different threads.
shared_ptr<X> p1( new X );
local_shared_ptr<X> p2( p1 ); // p2.local_use_count() == 1
local_shared_ptr<X> p3( p1 ); // p3.local_use_count() also 1
Creating the second local_shared_ptr
from the first one, however, does lead to the two sharing the same count:
shared_ptr<X> p1( new X );
local_shared_ptr<X> p2( p1 ); // p2.local_use_count() == 1
local_shared_ptr<X> p3( p2 ); // p3.local_use_count() == 2
Two shared_ptr
instances created from the same local_shared_ptr
do share ownership:
local_shared_ptr<X> p1( new X );
shared_ptr<X> p2( p1 ); // p2.use_count() == 2
shared_ptr<X> p3( p1 ); // p3.use_count() == 3
Here p2.use_count()
is 2, because p1
holds a reference, too.
One can think of local_shared_ptr<T>
as shared_ptr<shared_ptr<T>>
, with the outer shared_ptr
using non-atomic operations for
its count. Converting from local_shared_ptr
to shared_ptr
gives you a copy of the inner shared_ptr
; converting from shared_ptr
wraps it into an outer shared_ptr
with a non-atomic use count (conceptually speaking) and returns the result.
Synopsis
local_shared_ptr
is defined in <boost/smart_ptr/local_shared_ptr.hpp>
.
namespace boost {
template<class T> class local_shared_ptr {
public:
typedef /*see below*/ element_type;
// constructors
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
template<class Y> explicit local_shared_ptr(Y * p);
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
// destructor
~local_shared_ptr() noexcept;
// assignment
local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
local_shared_ptr & operator=(local_shared_ptr const && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
local_shared_ptr & operator=(std::nullptr_t) noexcept;
// reset
void reset() noexcept;
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
// accessors
T & operator*() const noexcept; // only valid when T is not an array type
T * operator->() const noexcept; // only valid when T is not an array type
// only valid when T is an array type
element_type & operator[](std::ptrdiff_t i) const noexcept;
element_type * get() const noexcept;
long local_use_count() const noexcept;
// conversions
explicit operator bool() const noexcept;
template<class Y> operator shared_ptr<Y>() const noexcept;
template<class Y> operator weak_ptr<Y>() const noexcept;
// swap
void swap(local_shared_ptr & b) noexcept;
// owner_before
template<class Y> bool owner_before(local_shared_ptr<Y> const & r) const noexcept;
// owner_equals
template<class Y> bool owner_equals(local_shared_ptr<Y> const & r) const noexcept;
};
// comparisons
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
// swap
template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
// get_pointer
template<class T>
typename local_shared_ptr<T>::element_type *
get_pointer(local_shared_ptr<T> const & p) noexcept;
// casts
template<class T, class U>
local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> const_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> dynamic_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> reinterpret_pointer_cast(local_shared_ptr<U> const & r) noexcept;
// stream I/O
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, local_shared_ptr<Y> const & p);
// get_deleter
template<class D, class T> D * get_deleter(local_shared_ptr<T> const & p) noexcept;
}
Members
element_type
typedef ... element_type;
element_type
is T
when T
is not an array type, and U
when T
is U[]
or U[N]
.
default constructor
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
-
- Effects
-
Constructs an empty
local_shared_ptr
. - Postconditions
-
local_use_count() == 0 && get() == 0
.
pointer constructor
template<class Y> explicit local_shared_ptr(Y * p);
-
- Effects
-
Constructs a
local_shared_ptr
that ownsshared_ptr<T>( p )
. - Postconditions
-
local_use_count() == 1 && get() == p
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained.
constructors taking a deleter
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
-
- Effects
-
Constructs a
local_shared_ptr
that ownsshared_ptr<T>( p, d )
. - Postconditions
-
local_use_count() == 1 && get() == p
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained.
template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
-
- Effects
-
Constructs a
local_shared_ptr
that ownsshared_ptr<T>( p, d, a )
. - Postconditions
-
local_use_count() == 1 && get() == p
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained.
copy and converting constructors
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
If
r
is empty, constructs an emptylocal_shared_ptr
; otherwise, constructs alocal_shared_ptr
that shares ownership withr
. - Postconditions
-
get() == r.get() && local_use_count() == r.local_use_count()
.
move constructors
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
Move-constructs a
local_shared_ptr
fromr
. - Postconditions
-
*this
contains the old value ofr
.r
is empty andr.get() == 0
.
shared_ptr constructor
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
-
- Effects
-
Constructs a
local_shared_ptr
that ownsr
. - Postconditions
-
local_use_count() == 1
.get()
returns the old value ofr.get()
. - Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained.
aliasing constructor
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- Effects
-
constructs a
local_shared_ptr
that shares ownership withr
and storesp
. - Postconditions
-
get() == p && local_use_count() == r.local_use_count()
.
aliasing move constructor
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- Effects
-
Move-constructs a
local_shared_ptr
fromr
, while storingp
instead. - Postconditions
-
get() == p
andlocal_use_count()
equals the old count ofr
.r
is empty andr.get() == 0
.
unique_ptr constructor
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
-
- Requires
-
Y*
should be convertible toT*
. - Effects
-
-
When
r.get() == 0
, equivalent tolocal_shared_ptr()
; -
Otherwise, constructs a
local_shared_ptr
that ownsshared_ptr<T>( std::move(r) )
.
-
- Throws
-
std::bad_alloc
, or an implementation-defined exception when a resource other than memory could not be obtained. - Exception safety
-
If an exception is thrown, the constructor has no effect.
destructor
~local_shared_ptr() noexcept;
-
- Effects
-
-
If
*this
is empty, or shares ownership with anotherlocal_shared_ptr
instance (local_use_count() > 1
), there are no side effects. -
Otherwise, destroys the owned
shared_ptr
.
-
assignment
local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
-
- Effects
-
Equivalent to
local_shared_ptr(r).swap(*this)
. - Returns
-
*this
.
local_shared_ptr & operator=(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- Effects
-
Equivalent to
local_shared_ptr(std::move(r)).swap(*this)
. - Returns
-
*this
.
local_shared_ptr & operator=(std::nullptr_t) noexcept;
-
- Effects
-
Equivalent to
local_shared_ptr().swap(*this)
. - Returns
-
*this
.
reset
void reset() noexcept;
-
- Effects
-
Equivalent to
local_shared_ptr().swap(*this)
.
template<class Y> void reset(Y * p);
-
- Effects
-
Equivalent to
local_shared_ptr(p).swap(*this)
.
template<class Y, class D> void reset(Y * p, D d);
-
- Effects
-
Equivalent to
local_shared_ptr(p, d).swap(*this)
.
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- Effects
-
Equivalent to
local_shared_ptr(p, d, a).swap(*this)
.
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- Effects
-
Equivalent to
local_shared_ptr(r, p).swap(*this)
.
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- Effects
-
Equivalent to
local_shared_ptr(std::move(r), p).swap(*this)
.
indirection
T & operator*() const noexcept;
-
- Requires
-
T
should not be an array type. - Returns
-
*get()
.
T * operator->() const noexcept;
-
- Requires
-
T
should not be an array type. - Returns
-
get()
.
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- Requires
-
T
should be an array type. The stored pointer must not be 0.i >= 0
. IfT
isU[N]
,i < N
. - Returns
-
get()[i]
.
get
element_type * get() const noexcept;
-
- Returns
-
The stored pointer.
local_use_count
long local_use_count() const noexcept;
-
- Returns
-
The number of
local_shared_ptr
objects,*this
included, that share ownership with*this
, or 0 when*this
is empty.
conversions
explicit operator bool() const noexcept;
-
- Returns
-
get() != 0
.
NoteOn C++03 compilers, the return value is of an unspecified type.
template<class Y> operator shared_ptr<Y>() const noexcept;
template<class Y> operator weak_ptr<Y>() const noexcept;
-
- Requires
-
T*
should be convertible toY*
. - Returns
-
a copy of the owned
shared_ptr
.
swap
void swap(local_shared_ptr & b) noexcept;
-
- Effects
-
Exchanges the contents of the two smart pointers.
owner_before
template<class Y> bool owner_before(local_shared_ptr<Y> const & r) const noexcept;
-
- Returns
-
See the description of
operator<
.
owner_equals
template<class Y> bool owner_equals(local_shared_ptr<Y> const & r) const noexcept;
-
- Returns
-
true
if and only if*this
andr
share ownership or are both empty.
Free Functions
comparison
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- Returns
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- Returns
-
a.get() != b.get()
.
template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
-
- Returns
-
p.get() == 0
.
template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
-
- Returns
-
p.get() != 0
.
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- Returns
-
An unspecified value such that
-
operator<
is a strict weak ordering as described in section [lib.alg.sorting] of the C++ standard; -
under the equivalence relation defined by
operator<
,!(a < b) && !(b < a)
, twolocal_shared_ptr
instances are equivalent if and only if they share ownership or are both empty.
-
Note
|
Allows local_shared_ptr objects to be used as keys in associative containers.
|
Note
|
The rest of the comparison operators are omitted by design. |
swap
template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
-
- Effects
-
Equivalent to
a.swap(b)
.
get_pointer
template<class T>
typename local_shared_ptr<T>::element_type *
get_pointer(local_shared_ptr<T> const & p) noexcept;
-
- Returns
-
p.get()
.
NoteProvided as an aid to generic programming. Used by mem_fn
.
static_pointer_cast
template<class T, class U>
local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
static_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
local_shared_ptr<T>( r, static_cast<typename local_shared_ptr<T>::element_type*>(r.get()) )
.
Caution
|
The seemingly equivalent expression local_shared_ptr<T>(static_cast<T*>(r.get())) will eventually
result in undefined behavior, attempting to delete the same object twice.
|
const_pointer_cast
template<class T, class U>
local_shared_ptr<T> const_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
const_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
local_shared_ptr<T>( r, const_cast<typename local_shared_ptr<T>::element_type*>(r.get()) )
.
dynamic_pointer_cast
template<class T, class U>
local_shared_ptr<T> dynamic_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
dynamic_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
-
When
dynamic_cast<typename local_shared_ptr<T>::element_type*>(r.get())
returns a nonzero valuep
,local_shared_ptr<T>(r, p)
; -
Otherwise,
local_shared_ptr<T>()
.
-
reinterpret_pointer_cast
template<class T, class U>
local_shared_ptr<T> reinterpret_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- Requires
-
The expression
reinterpret_cast<T*>( (U*)0 )
must be well-formed. - Returns
-
local_shared_ptr<T>( r, reinterpret_cast<typename local_shared_ptr<T>::element_type*>(r.get()) )
.
operator<<
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, local_shared_ptr<Y> const & p);
-
- Effects
-
os << p.get();
. - Returns
-
os
.
get_deleter
template<class D, class T>
D * get_deleter(local_shared_ptr<T> const & p) noexcept;
-
- Returns
-
If
*this
owns ashared_ptr
instancep
,get_deleter<D>( p )
, otherwise 0.
make_local_shared: Creating local_shared_ptr
Description
The function templates make_local_shared
and allocate_local_shared
provide
convenient, safe and efficient ways to create local_shared_ptr
objects. They
are analogous to make_shared
and allocate_shared
for shared_ptr
.
Synopsis
make_local_shared
and allocate_local_shared
are defined in
<boost/smart_ptr/make_local_shared.hpp>
.
namespace boost {
template<class T, class... Args> local_shared_ptr<T> make_local_shared(Args&&... args); template<class T, class A, class... Args> local_shared_ptr<T> allocate_local_shared(const A& a, Args&&... args);// T is not an array
// T is an array of unknown bounds template<class T> local_shared_ptr<T> make_local_shared(std::size_t n); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n);
// T is an array of known bounds template<class T> local_shared_ptr<T> make_local_shared(); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a);
// T is an array of unknown bounds template<class T> local_shared_ptr<T> make_local_shared(std::size_t n, const remove_extent_t<T>& v); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
// T is an array of known bounds template<class T> local_shared_ptr<T> make_local_shared(const remove_extent_t<T>& v); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a, const remove_extent_t<T>& v);
// T is not an array of known bounds template<class T> local_shared_ptr<T> make_local_shared_noinit(); template<class T, class A> local_shared_ptr<T> allocate_local_shared_noinit(const A& a);
// T is an array of unknown bounds template<class T> local_shared_ptr<T> make_local_shared_noinit(std::size_t n); template<class T, class A> local_shared_ptr<T> allocate_local_shared_noinit(const A& a, std::size_t n); }
Description
The requirements and effects of these functions are the same as make_shared
and allocate_shared
, except that a local_shared_ptr
is returned.
Generic Pointer Casts
Description
The pointer cast function templates (static_pointer_cast
,
dynamic_pointer_cast
, const_pointer_cast
, and reinterpret_pointer_cast
)
provide a way to write generic pointer castings for raw pointers,
std::shared_ptr
and std::unique_ptr
.
There is test and example code in pointer_cast_test.cpp
Rationale
Boost smart pointers usually overload those functions to provide a mechanism
to emulate pointers casts. For example, shared_ptr<T>
implements a static
pointer cast this way:
template<class T, class U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
Pointer cast functions templates are overloads of static_pointer_cast
,
dynamic_pointer_cast
, const_pointer_cast
, and reinterpret_pointer_cast
for raw pointers, std::shared_ptr
and std::unique_ptr
. This way when
developing pointer type independent classes, for example, memory managers or
shared memory compatible classes, the same code can be used for raw and smart
pointers.
Synopsis
The generic pointer casts are defined in <boost/pointer_cast.hpp>
.
namespace boost {
template<class T, class U> T* static_pointer_cast(U* p) noexcept;
template<class T, class U> T* dynamic_pointer_cast(U* p) noexcept;
template<class T, class U> T* const_pointer_cast(U* p) noexcept;
template<class T, class U> T* reinterpret_pointer_cast(U* p) noexcept;
template<class T, class U> std::shared_ptr<T>
static_pointer_cast(const std::shared_ptr<U>& p) noexcept;
template<class T, class U> std::shared_ptr<T>
dynamic_pointer_cast(const std::shared_ptr<U>& p) noexcept;
template<class T, class U> std::shared_ptr<T>
const_pointer_cast(const std::shared_ptr<U>& p) noexcept;
template<class T, class U> std::shared_ptr<T>
reinterpret_pointer_cast(const std::shared_ptr<U>& p) noexcept;
template<class T, class U> std::unique_ptr<T>
static_pointer_cast(std::unique_ptr<U>&& p) noexcept;
template<class T, class U> std::unique_ptr<T>
dynamic_pointer_cast(std::unique_ptr<U>&& p) noexcept;
template<class T, class U> std::unique_ptr<T>
const_pointer_cast(std::unique_ptr<U>&& p) noexcept;
template<class T, class U> std::unique_ptr<T>
reinterpret_pointer_cast(std::unique_ptr<U>&& p) noexcept;
}
Free Functions
static_pointer_cast
template<class T, class U> T* static_pointer_cast(U* p) noexcept;
-
- Returns
-
static_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
static_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- Returns
-
std::static_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
static_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- Requires
-
The expression
static_cast<T*>((U*)0)
must be well-formed. - Returns
-
std::unique_ptr<T>(static_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
Caution
|
The seemingly equivalent expression
std::unique_ptr<T>(static_cast<T*>(p.get())) will eventually result in
undefined behavior, attempting to delete the same object twice.
|
dynamic_pointer_cast
template<class T, class U> T* dynamic_pointer_cast(U* p) noexcept;
-
- Returns
-
dynamic_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
dynamic_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- Returns
-
std::dynamic_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
dynamic_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- Requires
-
The expression
static_cast<T*>((U*)0)
must be well-formed. -
T
must have a virtual destructor.- Returns
-
When
dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.get())
returns a non-zero value,std::unique_ptr<T>(dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.release()));
. -
Otherwise,
std::unique_ptr<T>()
.
const_pointer_cast
template<class T, class U> T* const_pointer_cast(U* p) noexcept;
-
- Returns
-
const_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
const_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- Returns
-
std::const_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
const_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- Requires
-
The expression
const_cast<T*>((U*)0)
must be well-formed. - Returns
-
std::unique_ptr<T>(const_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
reinterpret_pointer_cast
template<class T, class U> T* reinterpret_pointer_cast(U* p) noexcept;
-
- Returns
-
reinterpret_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
reinterpret_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- Returns
-
std::reinterpret_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
reinterpret_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- Requires
-
The expression
reinterpret_cast<T*>((U*)0)
must be well-formed. - Returns
-
std::unique_ptr<T>(reinterpret_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
Example
The following example demonstrates how the generic pointer casts help us create pointer independent code.
#include <boost/pointer_cast.hpp>
#include <boost/shared_ptr.hpp>
class base {
public:
virtual ~base() { }
};
class derived : public base { };
template<class Ptr>
void check_if_it_is_derived(const Ptr& ptr)
{
assert(boost::dynamic_pointer_cast<derived>(ptr) != 0);
}
int main()
{
base* ptr = new derived;
boost::shared_ptr<base> sptr(new derived);
check_if_it_is_derived(ptr);
check_if_it_is_derived(sptr);
delete ptr;
}
pointer_to_other
Description
The pointer_to_other
utility provides a way, given a source pointer type, to obtain a pointer of the same type
to another pointee type.
There is test/example code in pointer_to_other_test.cpp.
Rationale
When building pointer independent classes, like memory managers, allocators, or containers, there is often a need to
define pointers generically, so that if a template parameter represents a pointer (for example, a raw or smart pointer
to an int
), we can define another pointer of the same type to another pointee (a raw or smart pointer to a float
.)
template <class IntPtr> class FloatPointerHolder
{
// Let's define a pointer to a float
typedef typename boost::pointer_to_other
<IntPtr, float>::type float_ptr_t;
float_ptr_t float_ptr;
};
Synopsis
pointer_to_other
is defined in <boost/smart_ptr/pointer_to_other.hpp>
.
namespace boost {
template<class T, class U> struct pointer_to_other;
template<class T, class U,
template <class> class Sp>
struct pointer_to_other< Sp<T>, U >
{
typedef Sp<U> type;
};
template<class T, class T2, class U,
template <class, class> class Sp>
struct pointer_to_other< Sp<T, T2>, U >
{
typedef Sp<U, T2> type;
};
template<class T, class T2, class T3, class U,
template <class, class, class> class Sp>
struct pointer_to_other< Sp<T, T2, T3>, U >
{
typedef Sp<U, T2, T3> type;
};
template<class T, class U>
struct pointer_to_other< T*, U >
{
typedef U* type;
};
}
If these definitions are not correct for a specific smart pointer, we can define a specialization of pointer_to_other
.
Example
// Let's define a memory allocator that can
// work with raw and smart pointers
#include <boost/pointer_to_other.hpp>
template <class VoidPtr>
class memory_allocator
{
// Predefine a memory_block
struct block;
// Define a pointer to a memory_block from a void pointer
// If VoidPtr is void *, block_ptr_t is block*
// If VoidPtr is smart_ptr<void>, block_ptr_t is smart_ptr<block>
typedef typename boost::pointer_to_other
<VoidPtr, block>::type block_ptr_t;
struct block
{
std::size_t size;
block_ptr_t next_block;
};
block_ptr_t free_blocks;
};
As we can see, using pointer_to_other
we can create pointer independent code.
atomic_shared_ptr
Description
The class template atomic_shared_ptr<T>
implements the interface of std::atomic
for a contained value of type shared_ptr<T>
. Concurrent access to atomic_shared_ptr
is not a data race.
Synopsis
atomic_shared_ptr
is defined in <boost/smart_ptr/atomic_shared_ptr.hpp>
.
namespace boost {
template<class T> class atomic_shared_ptr {
private:
shared_ptr<T> p_; // exposition only
atomic_shared_ptr(const atomic_shared_ptr&) = delete;
atomic_shared_ptr& operator=(const atomic_shared_ptr&) = delete;
public:
constexpr atomic_shared_ptr() noexcept;
atomic_shared_ptr( shared_ptr<T> p ) noexcept;
atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;
bool is_lock_free() const noexcept;
shared_ptr<T> load( int = 0 ) const noexcept;
operator shared_ptr<T>() const noexcept;
void store( shared_ptr<T> r, int = 0 ) noexcept;
shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
};
}
Members
constexpr atomic_shared_ptr() noexcept;
-
- Effects
-
Default-initializes
p_
.
atomic_shared_ptr( shared_ptr<T> p ) noexcept;
-
- Effects
-
Initializes
p_
top
.
atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;
-
- Effects
-
p_.swap(r)
. - Returns
-
*this
.
bool is_lock_free() const noexcept;
-
- Returns
-
false
.
NoteThis implementation is not lock-free.
shared_ptr<T> load( int = 0 ) const noexcept;
operator shared_ptr<T>() const noexcept;
-
- Returns
-
p_
.
NoteThe int
argument is intended to be of typememory_order
, but is ignored. This implementation is lock-based and therefore always sequentially consistent.
void store( shared_ptr<T> r, int = 0 ) noexcept;
-
- Effects
-
p_.swap(r)
.
shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;
-
- Effects
-
p_.swap(r)
. - Returns
-
The old value of
p_
.
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
-
- Effects
-
If
p_
is equivalent tov
, assignsw
top_
, otherwise assignsp_
tov
. - Returns
-
true
ifp_
was equivalent tov
,false
otherwise. - Remarks
-
Two
shared_ptr
instances are equivalent if they store the same pointer value and share ownership.
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
-
- Effects
-
If
p_
is equivalent tov
, assignsstd::move(w)
top_
, otherwise assignsp_
tov
. - Returns
-
true
ifp_
was equivalent tov
,false
otherwise. - Remarks
-
The old value of
w
is not preserved in either case.
owner_less
Description
owner_less<T>
is a helper function object that compares two smart
pointer objects using owner_before
. It is only provided for compatibility
with C++11 and corresponds to the standard component of the same name.
When using Boost smart pointers, the use of owner_less
is unnecessary, as
the supplied operator<
overloads (and, correspondingly, std::less
) return
the same result.
Synopsis
owner_less
is defined in <boost/smart_ptr/owner_less.hpp>
.
namespace boost {
template<class T = void> struct owner_less
{
typedef bool result_type;
typedef T first_argument_type;
typedef T second_argument_type;
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
};
}
Members
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
-
- Returns
-
u.owner_before( v )
.
owner_equal_to
Description
owner_equal_to<T>
is a helper function object that compares two smart
pointer objects using owner_equals
.
Synopsis
owner_equal_to
is defined in <boost/smart_ptr/owner_equal_to.hpp>
.
namespace boost {
template<class T = void> struct owner_equal_to
{
typedef bool result_type;
typedef T first_argument_type;
typedef T second_argument_type;
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
};
}
Members
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
-
- Returns
-
u.owner_equals( v )
.
owner_hash
Description
owner_hash<T>
is a helper function object that takes a smart pointer p
and returns p.owner_hash_value()
. It’s useful for creating unordered
containers of shared_ptr
that use ownership-based equality, instead of
the default pointer value equality. (It can be used with weak_ptr
too,
but there’s no need, because boost::hash
and std::hash
for weak_ptr
already use ownership-based equality.)
Example
std::unordered_set< boost::shared_ptr<void>,
boost::owner_hash< boost::shared_ptr<void> >,
boost::owner_equal_to< boost::shared_ptr<void> > > set;
Synopsis
owner_hash
is defined in <boost/smart_ptr/owner_hash.hpp>
.
namespace boost {
template<class T> struct owner_hash
{
typedef std::size_t result_type;
typedef T argument_type;
std::size_t operator()( T const & p ) const noexcept;
};
}
Members
std::size_t operator()( T const & p ) const noexcept;
-
- Returns
-
p.owner_hash_value()
.
Appendix A: Smart Pointer Programming Techniques
Using incomplete classes for implementation hiding
A proven technique (that works in C, too) for separating interface from implementation is to use a pointer to an incomplete class as an opaque handle:
class FILE;
FILE * fopen(char const * name, char const * mode);
void fread(FILE * f, void * data, size_t size);
void fclose(FILE * f);
It is possible to express the above interface using shared_ptr
, eliminating the need to manually call fclose
:
class FILE;
shared_ptr<FILE> fopen(char const * name, char const * mode);
void fread(shared_ptr<FILE> f, void * data, size_t size);
This technique relies on shared_ptr
’s ability to execute a custom deleter, eliminating the explicit call to fclose
, and on the fact that shared_ptr<X>
can be copied and destroyed when X
is incomplete.
The "Pimpl" idiom
A C++ specific variation of the incomplete class pattern is the "Pimpl" idiom. The incomplete class is not exposed to the user; it is hidden behind a forwarding facade. shared_ptr
can be used to implement a "Pimpl":
// file.hpp:
class file
{
private:
class impl;
shared_ptr<impl> pimpl_;
public:
file(char const * name, char const * mode);
// compiler generated members are fine and useful
void read(void * data, size_t size);
};
// file.cpp:
#include "file.hpp"
class file::impl
{
private:
impl(impl const &);
impl & operator=(impl const &);
// private data
public:
impl(char const * name, char const * mode) { ... }
~impl() { ... }
void read(void * data, size_t size) { ... }
};
file::file(char const * name, char const * mode): pimpl_(new impl(name, mode))
{
}
void file::read(void * data, size_t size)
{
pimpl_->read(data, size);
}
The key thing to note here is that the compiler-generated copy constructor, assignment operator, and destructor all have a sensible meaning. As a result, file
is CopyConstructible
and Assignable
, allowing its use in standard containers.
Using abstract classes for implementation hiding
Another widely used C++ idiom for separating inteface and implementation is to use abstract base classes and factory functions.
The abstract classes are sometimes called "interfaces" and the pattern is known as "interface-based programming". Again,
shared_ptr
can be used as the return type of the factory functions:
// X.hpp:
class X
{
public:
virtual void f() = 0;
virtual void g() = 0;
protected:
~X() {}
};
shared_ptr<X> createX();
// X.cpp:
class X_impl: public X
{
private:
X_impl(X_impl const &);
X_impl & operator=(X_impl const &);
public:
virtual void f()
{
// ...
}
virtual void g()
{
// ...
}
};
shared_ptr<X> createX()
{
shared_ptr<X> px(new X_impl);
return px;
}
A key property of shared_ptr
is that the allocation, construction, deallocation, and destruction details are captured at the point of construction, inside the factory function.
Note the protected and nonvirtual destructor in the example above. The client code cannot, and does not need to, delete a pointer to X
; the shared_ptr<X>
instance returned from createX
will correctly call ~X_impl
.
Preventing delete px.get()
It is often desirable to prevent client code from deleting a pointer that is being managed by shared_ptr
. The previous technique showed one possible approach, using a protected destructor. Another alternative is to use a private deleter:
class X
{
private:
~X();
class deleter;
friend class deleter;
class deleter
{
public:
void operator()(X * p) { delete p; }
};
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X, X::deleter());
return px;
}
};
Encapsulating allocation details, wrapping factory functions
shared_ptr
can be used in creating C++ wrappers over existing C style library interfaces that return raw pointers from their factory functions
to encapsulate allocation details. As an example, consider this interface, where CreateX
might allocate X
from its own private heap, ~X
may
be inaccessible, or X
may be incomplete:
X * CreateX(); void DestroyX(X *);
The only way to reliably destroy a pointer returned by CreateX
is to call DestroyX
.
Here is how a shared_ptr
-based wrapper may look like:
shared_ptr<X> createX() { shared_ptr<X> px(CreateX(), DestroyX); return px; }
Client code that calls createX
still does not need to know how the object has been allocated, but now the destruction is automatic.
Using a shared_ptr to hold a pointer to a statically allocated object
Sometimes it is desirable to create a shared_ptr
to an already existing object, so that the shared_ptr
does not attempt to destroy the
object when there are no more references left. As an example, the factory function:
shared_ptr<X> createX();
in certain situations may need to return a pointer to a statically allocated X
instance.
The solution is to use a custom deleter that does nothing:
struct null_deleter
{
void operator()(void const *) const
{
}
};
static X x;
shared_ptr<X> createX()
{
shared_ptr<X> px(&x, null_deleter());
return px;
}
The same technique works for any object known to outlive the pointer.
Using a shared_ptr to hold a pointer to a COM Object
Background: COM objects have an embedded reference count and two member functions that manipulate it. AddRef()
increments the count.
Release()
decrements the count and destroys itself when the count drops to zero.
It is possible to hold a pointer to a COM object in a shared_ptr
:
shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p) { p->AddRef(); shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release)); return pw; }
Note, however, that shared_ptr
copies created from pw
will not "register" in the embedded count of the COM object;
they will share the single reference created in make_shared_from_COM
. Weak pointers created from pw
will be invalidated when the last
shared_ptr
is destroyed, regardless of whether the COM object itself is still alive.
As explained in the mem_fn
documentation, you need to #define BOOST_MEM_FN_ENABLE_STDCALL
first.
Using a shared_ptr to hold a pointer to an object with an embedded reference count
This is a generalization of the above technique. The example assumes that the object implements the two functions required by intrusive_ptr
,
intrusive_ptr_add_ref
and intrusive_ptr_release
:
template<class T> struct intrusive_deleter
{
void operator()(T * p)
{
if(p) intrusive_ptr_release(p);
}
};
shared_ptr<X> make_shared_from_intrusive(X * p)
{
if(p) intrusive_ptr_add_ref(p);
shared_ptr<X> px(p, intrusive_deleter<X>());
return px;
}
Using a shared_ptr to hold another shared ownership smart pointer
One of the design goals of shared_ptr
is to be used in library interfaces. It is possible to encounter a situation where a library takes a
shared_ptr
argument, but the object at hand is being managed by a different reference counted or linked smart pointer.
It is possible to exploit shared_ptr
’s custom deleter feature to wrap this existing smart pointer behind a shared_ptr
facade:
template<class P> struct smart_pointer_deleter
{
private:
P p_;
public:
smart_pointer_deleter(P const & p): p_(p)
{
}
void operator()(void const *)
{
p_.reset();
}
P const & get() const
{
return p_;
}
};
shared_ptr<X> make_shared_from_another(another_ptr<X> qx)
{
shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx));
return px;
}
One subtle point is that deleters are not allowed to throw exceptions, and the above example as written assumes that p_.reset()
doesn’t throw.
If this is not the case, p_.reset();
should be wrapped in a try {} catch(…) {}
block that ignores exceptions. In the (usually unlikely) event
when an exception is thrown and ignored, p_
will be released when the lifetime of the deleter ends. This happens when all references, including
weak pointers, are destroyed or reset.
Another twist is that it is possible, given the above shared_ptr
instance, to recover the original smart pointer, using get_deleter
:
void extract_another_from_shared(shared_ptr<X> px)
{
typedef smart_pointer_deleter< another_ptr<X> > deleter;
if(deleter const * pd = get_deleter<deleter>(px))
{
another_ptr<X> qx = pd->get();
}
else
{
// not one of ours
}
}
Obtaining a shared_ptr from a raw pointer
Sometimes it is necessary to obtain a shared_ptr
given a raw pointer to an object that is already managed by another shared_ptr
instance. Example:
void f(X * p) { shared_ptr<X> px(???); }
Inside f
, we’d like to create a shared_ptr
to *p
.
In the general case, this problem has no solution. One approach is to modify f
to take a shared_ptr
, if possible:
void f(shared_ptr<X> px);
The same transformation can be used for nonvirtual member functions, to convert the implicit this
:
void X::f(int m);
would become a free function with a shared_ptr
first argument:
void f(shared_ptr<X> this_, int m);
If f
cannot be changed, but X
uses intrusive counting, use make_shared_from_intrusive
described above. Or, if it’s known that the shared_ptr
created in f
will never outlive the object, use a null deleter.
Obtaining a shared_ptr (weak_ptr) to this in a constructor
Some designs require objects to register themselves on construction with a central authority. When the registration routines take a shared_ptr
, this leads to the question how could a constructor obtain a shared_ptr
to this
:
class X
{
public:
X()
{
shared_ptr<X> this_(???);
}
};
In the general case, the problem cannot be solved. The X
instance being constructed can be an automatic variable or a static variable; it can be created on the heap:
shared_ptr<X> px(new X);
but at construction time, px
does not exist yet, and it is impossible to create another shared_ptr
instance that shares ownership with it.
Depending on context, if the inner shared_ptr this_
doesn’t need to keep the object alive, use a null_deleter
as explained here and here.
If X
is supposed to always live on the heap, and be managed by a shared_ptr
, use a static factory function:
class X
{
private:
X() { ... }
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X);
// use px as 'this_'
return px;
}
};
Obtaining a shared_ptr to this
Sometimes it is needed to obtain a shared_ptr
from this
in a virtual member function under the assumption that this
is already managed by a shared_ptr
.
The transformations described in the previous technique cannot be applied.
A typical example:
class X
{
public:
virtual void f() = 0;
protected:
~X() {}
};
class Y
{
public:
virtual shared_ptr<X> getX() = 0;
protected:
~Y() {}
};
// --
class impl: public X, public Y
{
public:
impl() { ... }
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
shared_ptr<X> px(???);
return px;
}
};
The solution is to keep a weak pointer to this
as a member in impl
:
class impl: public X, public Y
{
private:
weak_ptr<impl> weak_this;
impl(impl const &);
impl & operator=(impl const &);
impl() { ... }
public:
static shared_ptr<impl> create()
{
shared_ptr<impl> pi(new impl);
pi->weak_this = pi;
return pi;
}
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
shared_ptr<X> px(weak_this);
return px;
}
};
The library now includes a helper class template enable_shared_from_this
that can be used to encapsulate the solution:
class impl: public X, public Y, public enable_shared_from_this<impl>
{
public:
impl(impl const &);
impl & operator=(impl const &);
public:
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
return shared_from_this();
}
}
Note that you no longer need to manually initialize the weak_ptr
member in enable_shared_from_this
. Constructing a shared_ptr
to impl
takes care of that.
Using shared_ptr as a smart counted handle
Some library interfaces use opaque handles, a variation of the incomplete class technique described above. An example:
typedef void * HANDLE;
HANDLE CreateProcess();
void CloseHandle(HANDLE);
Instead of a raw pointer, it is possible to use shared_ptr
as the handle and get reference counting and automatic resource management for free:
typedef shared_ptr<void> handle;
handle createProcess()
{
shared_ptr<void> pv(CreateProcess(), CloseHandle);
return pv;
}
Using shared_ptr to execute code on block exit
shared_ptr<void>
can automatically execute cleanup code when control leaves a scope.
-
Executing
f(p)
, wherep
is a pointer:shared_ptr<void> guard(p, f);
-
Executing arbitrary code:
f(x, y)
:shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y));
Using shared_ptr<void> to hold an arbitrary object
shared_ptr<void>
can act as a generic object pointer similar to void*
. When a shared_ptr<void>
instance constructed as:
shared_ptr<void> pv(new X);
is destroyed, it will correctly dispose of the X
object by executing ~X
.
This propery can be used in much the same manner as a raw void*
is used to temporarily strip type information from an object pointer.
A shared_ptr<void>
can later be cast back to the correct type by using static_pointer_cast
.
Associating arbitrary data with heterogeneous shared_ptr
instances
shared_ptr
and weak_ptr
support operator<
comparisons required by standard associative containers such as std::map
. This can be
used to non-intrusively associate arbitrary data with objects managed by shared_ptr
:
typedef int Data;
std::map<shared_ptr<void>, Data> userData;
// or std::map<weak_ptr<void>, Data> userData; to not affect the lifetime
shared_ptr<X> px(new X);
shared_ptr<int> pi(new int(3));
userData[px] = 42;
userData[pi] = 91;
Using shared_ptr
as a CopyConstructible
mutex lock
Sometimes it’s necessary to return a mutex lock from a function, and a noncopyable lock cannot be returned by value. It is possible to use shared_ptr
as a mutex lock:
class mutex
{
public:
void lock();
void unlock();
};
shared_ptr<mutex> lock(mutex & m)
{
m.lock();
return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}
Better yet, the shared_ptr
instance acting as a lock can be encapsulated in a dedicated shared_lock
class:
class shared_lock
{
private:
shared_ptr<void> pv;
public:
template<class Mutex> explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {}
};
shared_lock
can now be used as:
shared_lock lock(m);
Note that shared_lock
is not templated on the mutex type, thanks to shared_ptr<void>
’s ability to hide type information.
Using shared_ptr to wrap member function calls
shared_ptr
implements the ownership semantics required from the Wrap/CallProxy
scheme described in Bjarne Stroustrup’s article
"Wrapping C++ Member Function Calls" (available online at http://www.stroustrup.com/wrapper.pdf). An implementation is given below:
template<class T> class pointer
{
private:
T * p_;
public:
explicit pointer(T * p): p_(p)
{
}
shared_ptr<T> operator->() const
{
p_->prefix();
return shared_ptr<T>(p_, mem_fn(&T::suffix));
}
};
class X
{
private:
void prefix();
void suffix();
friend class pointer<X>;
public:
void f();
void g();
};
int main()
{
X x;
pointer<X> px(&x);
px->f();
px->g();
}
Delayed deallocation
In some situations, a single px.reset()
can trigger an expensive deallocation in a performance-critical region:
class X; // ~X is expensive
class Y
{
shared_ptr<X> px;
public:
void f()
{
px.reset();
}
};
The solution is to postpone the potential deallocation by moving px
to a dedicated free list that can be periodically emptied when performance and response times are not an issue:
vector< shared_ptr<void> > free_list;
class Y
{
shared_ptr<X> px;
public:
void f()
{
free_list.push_back(px);
px.reset();
}
};
// periodically invoke free_list.clear() when convenient
Another variation is to move the free list logic to the construction point by using a delayed deleter:
struct delayed_deleter
{
template<class T> void operator()(T * p)
{
try
{
shared_ptr<void> pv(p);
free_list.push_back(pv);
}
catch(...)
{
}
}
};
Weak pointers to objects not managed by a shared_ptr
Make the object hold a shared_ptr
to itself, using a null_deleter
:
class X
{
private:
shared_ptr<X> this_;
int i_;
public:
explicit X(int i): this_(this, null_deleter()), i_(i)
{
}
// repeat in all constructors (including the copy constructor!)
X(X const & rhs): this_(this, null_deleter()), i_(rhs.i_)
{
}
// do not forget to not assign this_ in the copy assignment
X & operator=(X const & rhs)
{
i_ = rhs.i_;
}
weak_ptr<X> get_weak_ptr() const { return this_; }
};
When the object’s lifetime ends, X::this_
will be destroyed, and all weak pointers will automatically expire.
Appendix B: History and Acknowledgments
Summer 1994
Greg Colvin proposed
to the C++ Standards Committee classes named auto_ptr
and counted_ptr
which were very
similar to what we now call scoped_ptr
and shared_ptr
. In one of the very few cases
where the Library Working Group’s recommendations were not followed by the full committee,
counted_ptr
was rejected and surprising transfer-of-ownership semantics were added to auto_ptr
.
October 1998
Beman Dawes proposed reviving the original semantics under the names safe_ptr
and counted_ptr
,
meeting of Per Andersson, Matt Austern, Greg Colvin, Sean Corfield, Pete Becker, Nico Josuttis,
Dietmar Kühl, Nathan Myers, Chichiang Wan and Judy Ward. During the discussion, the four new class
names were finalized, it was decided that there was no need to exactly follow the std::auto_ptr
interface, and various function signatures and semantics were finalized.
Over the next three months, several implementations were considered for shared_ptr
, and discussed
on the boost.org mailing list. The implementation questions revolved around
the reference count which must be kept, either attached to the pointed to object, or detached elsewhere.
Each of those variants have themselves two major variants:
-
Direct detached: the
shared_ptr
contains a pointer to the object, and a pointer to the count. -
Indirect detached: the
shared_ptr
contains a pointer to a helper object, which in turn contains a pointer to the object and the count. -
Embedded attached: the count is a member of the object pointed to.
-
Placement attached: the count is attached via operator new manipulations.
Each implementation technique has advantages and disadvantages. We went so far as to run various timings of the direct and indirect approaches, and found that at least on Intel Pentium chips there was very little measurable difference. Kevlin Henney provided a paper he wrote on "Counted Body Techniques." Dietmar Kühl suggested an elegant partial template specialization technique to allow users to choose which implementation they preferred, and that was also experimented with.
But Greg Colvin and Jerry Schwarz argued that "parameterization will discourage users", and in the end we choose to supply only the direct implementation.
May 1999
In April and May, 1999, Valentin Bonnard and David Abrahams made a number of suggestions resulting in numerous improvements.
September 1999
Luis Coelho provided shared_ptr::swap
and shared_array::swap
.
November 1999
Darin Adler provided operator ==
, operator !=
, and std::swap
and std::less
specializations for shared types.
May 2001
Vladimir Prus suggested requiring a complete type on destruction. Refinement evolved in discussions including Dave Abrahams, Greg Colvin, Beman Dawes, Rainer Deyke, Peter Dimov, John Maddock, Vladimir Prus, Shankar Sai, and others.
January 2002
Peter Dimov reworked all four classes, adding features, fixing bugs, splitting them into four separate headers, and adding
weak_ptr
.
March 2003
Peter Dimov, Beman Dawes and Greg Colvin proposed shared_ptr
and weak_ptr
for inclusion in the Standard Library via the first Library Technical Report (known as TR1). The proposal was
accepted and eventually went on to become a part of the C++ standard in its 2011 iteration.
July 2007
Peter Dimov and Beman Dawes proposed a number of enhancements
to shared_ptr
as it was entering the working paper that eventually became the C++11 standard.
November 2012
Glen Fernandes provided implementations of make_shared
and allocate_shared
for arrays. They achieve a single allocation
for an array that can be initialized with constructor arguments or initializer lists as well as overloads for default initialization
and no value initialization.
Peter Dimov aided this development by extending shared_ptr
to support arrays via the syntax shared_ptr<T[]>
and shared_ptr<T[N]>
.
April 2013
Peter Dimov proposed the extension of shared_ptr
to support
arrays for inclusion into the standard, and it was accepted.
February 2014
Glen Fernandes updated make_shared
and allocate_shared
to conform to the specification in C++ standard paper
N3870, and implemented make_unique
for arrays and objects.
Peter Dimov and Glen Fernandes updated the scalar and array implementations, respectively, to resolve C++ standard library defect 2070.
February 2017
Glen Fernandes rewrote allocate_shared
and make_shared
for arrays for a more optimal and more maintainable implementation.
June 2017
Peter Dimov and Glen Fernandes rewrote the documentation in Asciidoc format.
Peter Dimov added atomic_shared_ptr
and local_shared_ptr
.
August 2019
Glen Fernandes implemented allocate_unique
for scalars and arrays.
Appendix C: shared_array (deprecated)
Note
|
This facility is deprecated because a shared_ptr to T[] or T[N]
is now available, and is superior in every regard.
|
Description
The shared_array
class template stores a pointer to a dynamically allocated
array. (Dynamically allocated array are allocated with the C++ new[]
expression.) The object pointed to is guaranteed to be deleted when the last
shared_array
pointing to it is destroyed or reset.
Every shared_array
meets the CopyConstructible and Assignable
requirements of the C++ Standard Library, and so can be used in standard
library containers. Comparison operators are supplied so that shared_array
works with the standard library’s associative containers.
Normally, a shared_array
cannot correctly hold a pointer to an object that
has been allocated with the non-array form of new
. See shared_ptr
for that
usage.
Because the implementation uses reference counting, cycles of shared_array
instances will not be reclaimed. For example, if main
holds a shared_array
to A
, which directly or indirectly holds a shared_array back to A
, the use
count of A
will be 2. Destruction of the original shared_array
will leave
A
dangling with a use count of 1.
A shared_ptr
to a std::vector
is an alternative to a shared_array
that
is a bit heavier duty but far more flexible.
The class template is parameterized on T
, the type of the object pointed to.
shared_array
and most of its member functions place no requirements on T
;
it is allowed to be an incomplete type, or void
. Member functions that do
place additional requirements (constructors, reset) are explicitly documented
below.
Synopsis
namespace boost {
template<class T> class shared_array {
public:
typedef T element_type;
explicit shared_array(T* p = 0);
template<class D> shared_array(T* p, D d);
shared_array(const shared_array& v) noexcept;
~shared_array() noexcept;
shared_array& operator=(const shared_array& v) noexcept;
void reset(T* p = 0);
template<class D> void reset(T* p, D d);
T& operator[](std::ptrdiff_t n) const noexcept;
T* get() const noexcept;
bool unique() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
void swap(shared_array<T>& v) noexcept;
};
template<class T> bool
operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
}
Members
element_type
typedef T element_type;
- Type
-
Provides the type of the stored pointer.
Constructors
explicit shared_array(T* p = 0);
-
- Effects
-
Constructs a
shared_array
, storing a copy ofp
, which must be a pointer to an array that was allocated via a C++new[]
expression or be 0. Afterwards, the use count is 1 (even ifp == 0
; see~shared_array
). - Requires
-
T
is a complete type. - Throws
-
std::bad_alloc
. If an exception is thrown,delete[] p
is called.
template<class D> shared_array(T* p, D d);
-
- Effects
-
Constructs a
shared_array
, storing a copy ofp
and ofd
. Afterwards, the use count is 1. When the the time comes to delete the array pointed to byp
, the objectd
is used in the statementd(p)
. - Requires
-
T
is a complete type. -
The copy constructor and destructor of
D
must not throw. -
Invoking the object
d
with parameterp
must not throw.- Throws
-
std::bad_alloc
. If an exception is thrown,d(p)
is called.
shared_array(const shared_array& v) noexcept;
-
- Effects
-
Constructs a
shared_array
, as if by storing a copy of the pointer stored inv
. Afterwards, the use count for all copies is 1 more than the initial use count. - Requires
-
T
is a complete type.
Destructor
~shared_array() noexcept;
-
- Effects
-
Decrements the use count. Then, if the use count is 0, deletes the array pointed to by the stored pointer. Note that
delete[]
on a pointer with a value of 0 is harmless.
Assignment
shared_array& operator=(const shared_array& v) noexcept;
-
- Effects
-
Constructs a new
shared_array
as described above, then replaces thisshared_array
with the new one, destroying the replaced object. - Requires
-
T
is a complete type. - Returns
-
*this
.
reset
void reset(T* p = 0);
-
- Effects
-
Constructs a new
shared_array
as described above, then replaces thisshared_array
with the new one, destroying the replaced object. - Requires
-
T
is a complete type. - Throws
-
std::bad_alloc
. If an exception is thrown,delete[] p
is called.
template<class D> void reset(T* p, D d);
-
- Effects
-
Constructs a new
shared_array
as described above, then replaces thisshared_array
with the new one, destroying the replaced object. - Requires
-
T
is a complete type. -
The copy constructor of
D
must not throw.- Throws
-
std::bad_alloc
. If an exception is thrown,d(p)
is called.
Indexing
T& operator[](std::ptrdiff_t n) const noexcept;
- Returns
-
A reference to element
n
of the array pointed to by the stored pointer. Behavior is undefined and almost certainly undesirable if the stored pointer is 0, or ifn
is less than 0 or is greater than or equal to the number of elements in the array. - Requires
-
T
is a complete type.
get
T* get() const noexcept;
-
- Returns
-
The stored pointer.
unique
bool unique() const noexcept;
-
- Returns
-
true
if no othershared_array
is sharing ownership of the stored pointer,false
otherwise.
use_count
long use_count() const noexcept;
-
- Returns
-
The number of
shared_array
objects sharing ownership of the stored pointer.
Conversions
explicit operator bool() const noexcept;
-
- Returns
-
get() != 0
. - Requires
-
T
is a complete type.
swap
void swap(shared_array<T>& b) noexcept;
-
- Effects
-
Exchanges the contents of the two smart pointers.
Free Functions
Comparison
template<class T> bool
operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
-
- Returns
-
The result of comparing the stored pointers of the two smart pointers.
Note
|
The operator< overload is provided to define an ordering so that
shared_array objects can be used in associative containers such as
std::map . The implementation uses std::less<T*> to perform the comparison.
This ensures that the comparison is handled correctly, since the standard
mandates that relational operations on pointers are unspecified (5.9
[expr.rel] paragraph 2) but std::less on pointers is well-defined (20.3.3
[lib.comparisons] paragraph 8).
|
swap
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
-
- Returns
-
a.swap(b)
. - Requires
-
T
is a complete type.
Appendix D: Copyright and License
This documentation is
-
Copyright 1999 Greg Colvin
-
Copyright 1999 Beman Dawes
-
Copyright 2002 Darin Adler
-
Copyright 2003-2020 Peter Dimov
-
Copyright 2005, 2006 Ion Gaztañaga
-
Copyright 2008 Frank Mori Hess
-
Copyright 2012-2017 Glen Fernandes
-
Copyright 2013 Andrey Semashev
and is distributed under the Boost Software License, Version 1.0.