引言
智能指针是存储指向动态分配(堆)对象的指针的对象。它们的行为与内置 C++ 指针非常相似,不同之处在于它们会在适当的时间自动删除指向的对象。智能指针在出现异常时特别有用,因为它们确保正确销毁动态分配的对象。它们还可以用于跟踪由多个所有者共享的动态分配的对象。
从概念上讲,智能指针被视为拥有指向的对象,因此负责在不再需要该对象时将其删除。因此,它们是 Bjarne Stroustrup 的“C++ 编程语言”第 3 版第 14.4 节“资源管理”中描述的“资源获取即初始化”惯用法的示例。
此库提供了六个智能指针类模板
-
scoped_ptr
,用于将动态分配对象的拥有权包含到当前作用域; -
scoped_array
,它为动态分配的数组提供作用域所有权; -
shared_ptr
,一个用于管理对象或数组的共享所有权的多功能工具; -
weak_ptr
,一个指向由 shared_ptr 管理的对象的非拥有者观察者,可以临时提升为 shared_ptr; -
intrusive_ptr
,指向具有嵌入式引用计数的对象的指针; -
local_shared_ptr
,在单线程内提供共享所有权。
shared_ptr
和 weak_ptr
自 2011 年起就是 C++ 标准的一部分。
此外,该库还包含以下支持实用程序函数和类
-
make_shared
和allocate_shared
,用于创建返回 shared_ptr 的对象的工厂函数; -
make_unique
,一个返回std::unique_ptr
的工厂函数; -
allocate_unique
,一个使用分配器创建对象的工厂函数,返回std::unique_ptr
; -
enable_shared_from_this
,一个辅助基类,允许获取指向 this 的 shared_ptr; -
enable_shared_from
,enable_shared_from_this
的更新和更好的替代方案; -
pointer_to_other
,一个用于将一种智能指针类型转换为另一种类型的辅助特性; -
static_pointer_cast
及其配套项,泛型智能指针转换; -
intrusive_ref_counter
,一个包含引用计数的辅助基类。 -
atomic_shared_ptr
,一个辅助类,为类型为 shared_ptr 的值实现 std::atomic 的接口。
一般来说,库中指针管理的对象的析构函数或 operator delete 不允许抛出异常。
修订历史
1.87.0 中的更改
-
C++03 已不再支持,需要 C++11 编译器。这包括 GCC 4.8 或更高版本,以及 MSVC 14.0 或更高版本。
-
宏
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
和BOOST_SP_USE_PTHREADS
启用的功能已被弃用,并将 在未来的版本中删除对其的支持。
1.79.0 中的更改
-
添加了
get_allocator_pointer
1.74.0 中的更改
-
向
shared_ptr
、weak_ptr
、local_shared_ptr
添加了owner_equals
-
向
shared_ptr
、weak_ptr
添加了owner_hash_value
-
添加了
owner_equal_to
、owner_hash
-
为
shared_ptr
、local_shared_ptr
添加了std::hash
特化 -
为
weak_ptr
添加了boost::hash
支持,以及std::hash
、std::equal_to
特化
1.72.0 中的更改
-
添加了
allocate_unique
1.71.0 中的更改
-
向
weak_ptr
添加了别名构造函数 -
添加了
weak_ptr
::empty() -
添加了
enable_shared_from
、shared_from
和weak_from
1.65.0 中的更改
-
添加了
atomic_shared_ptr
-
添加了
local_shared_ptr
、make_local_shared
scoped_ptr:作用域对象所有权
描述
scoped_ptr
类模板存储指向动态分配对象的指针。(动态分配的对象使用 C++ new
表达式分配。)保证删除指向的对象,无论是通过 scoped_ptr
的销毁,还是通过显式 reset
。参见示例。
scoped_ptr
是针对简单需求的简单解决方案。它提供了一个基本的“资源获取即初始化”功能,没有共享所有权或所有权转移语义。它的名称和语义强制执行(通过不可复制)都表明其意图仅在当前作用域内保留所有权。因为它不可复制,所以对于不应复制的指针来说,它比 shared_ptr
更安全。
由于 scoped_ptr
很简单,在其通常的实现中,每个操作都与内置指针一样快,并且它没有比内置指针更多的空间开销。
scoped_ptr
不能用于 C++ 标准库容器。如果需要可以使用的智能指针,请使用 shared_ptr
或 std::unique_ptr
。
scoped_ptr
不能正确保存指向动态分配数组的指针。有关此用法,请参见 scoped_array
。
类模板的参数化为 T
,即指向的对象的类型。销毁 T
必须不抛出异常,并且在实例化 scoped_ptr
时,T
必须是完整的类型。
概要
scoped_ptr
定义在 <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;
}
成员
element_type
typedef T element_type;
提供存储指针的类型。
构造函数
explicit scoped_ptr(T * p = 0) noexcept;
构造一个 scoped_ptr
,存储 p
的副本,p
必须通过 C++ new
表达式分配或为 0。T
不需要是完整类型。
析构函数
~scoped_ptr() noexcept;
销毁存储指针指向的对象(如果有),如同使用 delete this->get()
一样。T
必须是完整类型。
reset
void reset(T * p = 0) noexcept;
删除存储指针指向的对象,然后存储 p
的副本,p
必须通过 C++ new
表达式分配或为 0。
由于需要删除之前的对象,因此 T
必须是完整类型。
间接寻址
T & operator*() const noexcept;
返回存储指针指向的对象的引用。如果存储指针为 0,则行为未定义。
T * operator->() const noexcept;
返回存储的指针。如果存储指针为 0,则行为未定义。
get
T * get() const noexcept;
返回存储的指针。T
不需要是完整类型。
转换
explicit operator bool () const noexcept; // never throws
返回 get() != 0
。
注意
|
在 C++03 编译器上,返回值的类型未指定。 |
swap
void swap(scoped_ptr & b) noexcept;
交换两个智能指针的内容。T
不需要是完整类型。
自由函数
swap
template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) noexcept;
相当于 a.swap(b)
。
比较
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;
返回 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;
返回 p.get() != nullptr
。
示例
这是一个使用 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';
}
示例程序产生了儿童童谣的开头
1
2
Buckle my shoe
基本原理
使用 scoped_ptr
而不是 std::auto_ptr
或 std::unique_ptr
的主要原因是让代码的读者知道您打算仅对当前作用域应用“资源获取即初始化”,并且没有意图转移所有权。
使用 scoped_ptr
的次要原因是防止以后的维护程序员添加一个通过返回 auto_ptr
来转移所有权的函数,因为维护程序员看到了 auto_ptr
,并假设所有权可以安全地转移。
考虑 bool
与 int
。我们都知道在幕后 bool
通常只是一个 int
。事实上,一些人反对在 C++ 标准中包含 bool,原因就在于此。但是通过编写 bool
而不是 int
,您可以告诉读者您的意图是什么。scoped_ptr
也一样;通过使用它,您就是在表达意图。
有人建议scoped_ptr<T>
等价于std::auto_ptr<T> const
。然而,Ed Brey 指出,reset
方法无法作用于std::auto_ptr<T> const
。
句柄/主体惯用法
scoped_ptr
的一个常见用法是实现句柄/主体(也称为pimpl)惯用法,从而避免在头文件中暴露主体(实现)。
示例程序scoped_ptr_example_test.cpp
包含一个头文件scoped_ptr_example.hpp
,该文件使用scoped_ptr<>
指向一个不完整类型来隐藏实现。需要完整类型的成员函数的实例化发生在scoped_ptr_example.cpp
实现文件中。
常见问题
-
为什么
scoped_ptr
没有release()
成员?阅读源代码时,能够根据使用的类型推断程序行为非常有价值。如果
scoped_ptr
有release()
成员,则可以转移被持有指针的所有权,削弱其将资源生命周期限制在给定上下文中的作用。在需要转移所有权的情况下,使用std::auto_ptr
。(Dave Abrahams提供)
scoped_array:作用域数组所有权
描述
scoped_array
类模板存储指向动态分配数组的指针。(动态分配数组使用C++ new[]
表达式分配。)指向的数组保证会被删除,无论是在scoped_array
销毁时,还是通过显式reset
。
scoped_array
模板是针对简单需求的简单解决方案。它提供了一个基本的“资源获取即初始化”功能,没有共享所有权或所有权转移语义。它的名称和语义强制(不可复制)都表明其意图仅在当前作用域内保留所有权。由于它是不可复制的,对于不应该复制的指针,它比shared_ptr<T[]>
更安全。
由于scoped_array
非常简单,在其通常的实现中,每个操作都与内置数组指针一样快,并且它没有比内置数组指针更多的空间开销。
它不能用于C++标准库容器。如果scoped_array
不能满足您的需求,请参见shared_ptr<T[]>
。
它不能正确地持有指向单个对象的指针。对于这种用法,请参见scoped_ptr
。
std::vector
是scoped_array
的替代方案,它功能更强大,但灵活性更高。boost::array
是一个不使用动态分配的替代方案。
类模板的参数化对象为T
,即指向的对象的类型。
概要
scoped_array
定义在<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;
}
成员
element_type
typedef T element_type;
提供存储指针的类型。
构造函数
explicit scoped_array(T * p = 0) noexcept;
构造一个scoped_array
,存储p
的副本,p
必须通过C++ new[]
表达式分配或为0。T
不需要是完整类型。
析构函数
~scoped_array() noexcept;
删除存储的指针指向的数组。请注意,对值为0的指针进行delete[]
操作是无害的。T
必须是完整的类型,并且对存储的指针进行delete[]
操作不能抛出异常。
reset
void reset(T * p = 0) noexcept;
删除存储的指针指向的数组,然后存储p
的副本,p
必须通过C++ new[]
表达式分配或为0。T
必须是完整的类型,并且对存储的指针进行delete[]
操作不能抛出异常。
下标运算
T & operator[](std::ptrdiff_t i) const noexcept;
返回存储的指针指向的数组中元素i
的引用。如果存储的指针为0,或者i
小于0或大于等于数组中的元素数量,则行为未定义,几乎肯定是不希望的。
get
T * get() const noexcept;
返回存储的指针。T
不需要是完整类型。
转换
explicit operator bool () const noexcept;
返回 get() != 0
。
注意
|
在 C++03 编译器上,返回值的类型未指定。 |
swap
void swap(scoped_array & b) noexcept;
交换两个智能指针的内容。T
不需要是完整类型。
自由函数
swap
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
相当于 a.swap(b)
。
比较
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;
返回 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;
返回 p.get() != nullptr
。
shared_ptr:共享所有权
描述
shared_ptr
类模板存储指向动态分配对象的指针,通常使用C++ new
表达式。当指向它的最后一个shared_ptr
被销毁或重置时,保证会删除指向的对象。
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );
shared_ptr
删除在构造时传入的精确指针,包括其原始类型,而不管模板参数是什么。在上例的第二个例子中,当p2
被销毁或重置时,它将对最初传递给构造函数的int*
调用delete
,即使p2
本身是shared_ptr<void>
类型并存储void*
类型的指针。
每个shared_ptr
都满足C++标准库的CopyConstructible
、MoveConstructible
、CopyAssignable
和MoveAssignable
要求,并且可以用于标准库容器。提供比较运算符,以便shared_ptr
与标准库的关联容器一起使用。
由于实现使用了引用计数,因此shared_ptr
实例的循环将不会被回收。例如,如果main()
持有指向A
的shared_ptr
,而A
直接或间接地持有指向A
的shared_ptr
,则A
的使用计数将为2。原始shared_ptr
的销毁将留下使用计数为1的悬空A
。使用weak_ptr
来“打破循环”。
类模板的参数化对象为T
,即指向的对象的类型。shared_ptr
及其大多数成员函数对T
没有要求;它可以是不完整类型或void
。确实会施加附加要求的成员函数(构造函数、reset
)在下面有明确的说明。
只要T*
可以隐式转换为U*
,shared_ptr<T>
就可以隐式转换为shared_ptr<U>
。特别是,shared_ptr<T>
可以隐式转换为shared_ptr<T const>
,转换为shared_ptr<U>
(其中U
是T
的可访问基类),以及转换为shared_ptr<void>
。
shared_ptr
现在是C++11标准的一部分,名为std::shared_ptr
。
从Boost 1.53版本开始,shared_ptr
可以用来保存指向动态分配数组的指针。这是通过使用数组类型(T[]
或T[N]
)作为模板参数来实现的。使用无大小数组T[]
和大小数组T[N]
几乎没有区别;后者只是使operator[]
能够对索引执行范围检查。
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );
最佳实践
一个简单的指导原则,几乎消除了内存泄漏的可能性:始终使用命名的智能指针变量来保存new
的结果。代码中new
关键字的每次出现都应采用以下形式:
shared_ptr<T> p(new Y);
当然,可以使用另一个智能指针代替上面的shared_ptr
;T
和Y
是相同的类型,或者将参数传递给Y
的构造函数也是可以的。
如果您遵循此指导原则,那么自然地,您将没有显式的delete
语句;try
/catch
结构将很少见。
避免使用未命名的shared_ptr
临时变量来节省输入;要了解为什么这样做很危险,请考虑以下示例:
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() );
}
函数ok
严格遵循该指导原则,而bad
原地构造了临时shared_ptr
,可能会导致内存泄漏。由于函数参数的求值顺序未指定,因此new int(2)
可能先求值,g()
后求值,如果g
抛出异常,我们可能永远不会到达shared_ptr
构造函数。有关更多信息,请参见Herb Sutter对该问题的论述。
上面描述的异常安全问题也可以通过使用<boost/smart_ptr/make_shared.hpp>
中定义的make_shared
或allocate_shared
工厂函数来消除。这些工厂函数还通过合并分配来提高效率。
概要
shared_ptr
定义在<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;
}
成员
element_type
typedef ... element_type;
当T
不是数组类型时,element_type
为T
;当T
为U[]
或U[N]
时,element_type
为U
。
默认构造函数
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
-
- 效果
-
构造一个空的
shared_ptr
。 - 后置条件
-
.use_count() == 0 && get() == 0
指针构造函数
template<class Y> explicit shared_ptr(Y * p);
-
- 要求
-
Y
必须是完整类型。当T
是数组类型时,表达式delete[] p
;当T
不是数组类型时,表达式delete p
必须是良构的、定义良好的,并且不能抛出异常。当T
为U[N]
时,Y(*)[N]
必须可转换为T*
;当T
为U[]
时,Y(*)[]
必须可转换为T*
;否则,Y*
必须可转换为T*
。 - 效果
-
当
T
不是数组类型时,构造一个拥有指针p
的shared_ptr
。否则,构造一个拥有p
和一个未指定类型的析构器的shared_ptr
,该析构器调用delete[] p
。 - 后置条件
-
use_count() == 1 && get() == p
。如果T
不是数组类型并且p
可以明确转换为某个V
的enable_shared_from_this<V>*
,则p->shared_from_this()
返回*this
的副本。 - 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。 - 异常安全性
-
如果抛出异常,构造函数将调用
delete[] p
(当T
是数组类型时),或者调用delete p
(当T
不是数组类型时)。
注意
|
p 必须是指向通过C++ new 表达式分配的对象的指针,或者为0。即使p 为0,使用计数为1的后置条件也成立;对值为0的指针调用delete 是无害的。 |
注意
|
此构造函数是一个模板,以便记住传递的实际指针类型。即使T 没有虚析构函数或为void ,析构函数也将使用相同的指针调用delete,包括其原始类型。 |
带有析构器的构造函数
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);
-
- 要求
-
D
必须是CopyConstructible
。D
的复制构造函数和析构函数不能抛出异常。表达式d(p)
必须是良构的、定义良好的,并且不能抛出异常。A
必须是分配器,如C++标准的[allocator.requirements]部分中所述。当T
为U[N]
时,Y(*)[N]
必须可转换为T*
;当T
为U[]
时,Y(*)[]
必须可转换为T*
;否则,Y*
必须可转换为T*
。 - 效果
-
构造一个拥有指针
p
和析构器d
的shared_ptr
。带有分配器的构造函数使用a
的副本分配内存。 - 后置条件
-
use_count() == 1 && get() == p
。如果T
不是数组类型并且p
可以明确转换为某个V
的enable_shared_from_this<V>*
,则p->shared_from_this()
返回*this
的副本。 - 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。 - 异常安全性
-
如果抛出异常,则调用
d(p)
。
注意
|
当需要删除p 指向的对象时,将使用存储的d 的副本以及存储的p 的副本作为参数来调用它。 |
注意
|
自定义析构器允许返回shared_ptr 的工厂函数将用户与内存分配策略隔离开来。由于析构器不是类型的一部分,因此更改分配策略不会破坏源代码或二进制兼容性,也不需要客户端重新编译。例如,“无操作”析构器在返回指向静态分配对象的shared_ptr 时很有用,其他变体允许shared_ptr 用作另一个智能指针的包装器,从而简化互操作性。 |
注意
|
D 的复制构造函数不抛出异常的要求源于值传递。如果复制构造函数抛出异常,则指针将会泄漏。 |
复制构造函数和转换构造函数
shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
如果
r
为空,则构造一个空的shared_ptr
;否则,构造一个与r
共享所有权的shared_ptr
。 - 后置条件
-
.get() == r.get() && use_count() == r.use_count()
移动构造函数
shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
从
r
移动构造一个shared_ptr
。 - 后置条件
-
*this
包含r
的旧值。r
为空且r.get() == 0
。
别名构造函数
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
从
r
复制构造一个shared_ptr
,同时存储p
。 - 后置条件
-
.get() == p && use_count() == r.use_count()
别名移动构造函数
template<class Y> shared_ptr(shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
从
r
移动构造一个shared_ptr
,同时存储p
。 - 后置条件
-
get() == p
且use_count()
等于r
的旧计数。r
为空且r.get() == 0
。
weak_ptr
构造函数
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
构造一个与
r
共享所有权的shared_ptr
,并存储r
中存储的指针的副本。 - 后置条件
-
.use_count() == r.use_count()
- 抛出
-
当
r.use_count() == 0
时,抛出bad_weak_ptr
异常。 - 异常安全性
-
如果抛出异常,则构造函数无效。
auto_ptr
构造函数
template<class Y> shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
构造一个
shared_ptr
,如同存储r.release()
的副本一样。 - 后置条件
-
.use_count() == 1
- 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。 - 异常安全性
-
如果抛出异常,则构造函数无效。
unique_ptr
构造函数
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
-
当
r.get() == 0
时,等效于shared_ptr()
; -
当
D
不是引用类型时,等效于shared_ptr(r.release(), r.get_deleter())
; -
否则,等效于
shared_ptr(r.release(), del)
,其中del
是一个删除器,它存储从r.get_deleter()
返回的引用rd
,并且del(p)
调用rd(p)
。
-
- 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。 - 异常安全性
-
如果抛出异常,则构造函数无效。
析构函数
~shared_ptr() noexcept;
-
- 效果
-
-
如果
*this
为空,或者与另一个shared_ptr
实例共享所有权(use_count() > 1
),则没有副作用。 -
否则,如果
*this
拥有指针p
和删除器d
,则调用d(p)
。 -
否则,
*this
拥有指针p
,则调用delete p
。
-
赋值
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);
-
- 效果
-
等效于
shared_ptr(r).swap(*this)
。 - 返回值
-
.*this
注意
|
由临时对象构造和析构引起的引用计数更新不被认为是可观察的副作用,实现可以自由地通过不同的方式满足效果(以及隐含的保证),而无需创建临时对象。 |
注意
|
特别是,在以下示例中
这两个赋值都可能是空操作。 |
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);
-
- 效果
-
等效于
shared_ptr(std::move(r)).swap(*this)
。 - 返回值
-
.*this
shared_ptr & operator=(std::nullptr_t) noexcept;
-
- 效果
-
等效于
shared_ptr().swap(*this)
。 - 返回值
-
.*this
reset
void reset() noexcept;
-
- 效果
-
等效于
shared_ptr().swap(*this)
。
template<class Y> void reset(Y * p);
-
- 效果
-
等效于
shared_ptr(p).swap(*this)
。
template<class Y, class D> void reset(Y * p, D d);
-
- 效果
-
等效于
shared_ptr(p, d).swap(*this)
。
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- 效果
-
等效于
shared_ptr(p, d, a).swap(*this)
。
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
等效于
shared_ptr(r, p).swap(*this)
。
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
等效于
shared_ptr(std::move(r), p).swap(*this)
。
间接寻址
T & operator*() const noexcept;
-
- 要求
-
T
不应该是数组类型。存储的指针不能为0。 - 返回值
-
.*get()
T * operator->() const noexcept;
-
- 要求
-
T
不应该是数组类型。存储的指针不能为0。 - 返回值
-
.get()
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- 要求
-
T
应该是数组类型。存储的指针不能为0。i >= 0
。如果T
是U[N]
,则i < N
。 - 返回值
-
.get()[i]
get
element_type * get() const noexcept;
-
- 返回值
-
存储的指针。
唯一性
bool unique() const noexcept;
-
- 返回值
-
.use_count() == 1
use_count
long use_count() const noexcept;
-
- 返回值
-
包括
*this
在内,共享*this
所有权的shared_ptr
对象的个数,或者当*this
为空时为0。
转换
explicit operator bool() const noexcept;
-
- 返回值
-
.get() != 0
注意此转换运算符允许在布尔上下文中使用 shared_ptr
对象,例如if(p && p->valid()) {}
。
注意
|
转换为bool 不仅仅是语法糖。它允许在使用dynamic_pointer_cast 或weak_ptr::lock 时在条件中声明shared_ptr 变量。 |
注意
|
在 C++03 编译器上,返回值的类型未指定。 |
swap
void swap(shared_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
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;
-
- 返回值
-
参见
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;
-
- 返回值
-
当且仅当
*this
和r
共享所有权或都为空时为true
。
owner_hash_value
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;
-
- 返回值
-
.a.get() == b.get()
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 返回值
-
.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;
-
- 返回值
-
.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;
-
- 返回值
-
.p.get() != 0
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 返回值
-
一个未指定的值,使得
-
operator<
是C++标准[lib.alg.sorting]节中描述的严格弱排序; -
在由
operator<
定义的等价关系下,!(a < b) && !(b < a)
,两个shared_ptr
实例等价当且仅当它们共享所有权或都为空。
-
注意
|
允许将shared_ptr 对象用作关联容器中的键。 |
注意
|
出于设计原因,省略了其余的比较运算符。 |
swap
template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
-
- 效果
-
相当于
a.swap(b)
。
get_pointer
template<class T>
typename shared_ptr<T>::element_type *
get_pointer(shared_ptr<T> const & p) noexcept;
-
- 返回值
-
.p.get()
注意作为泛型编程的辅助工具提供。被 mem_fn
使用。
static_pointer_cast
template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
static_cast
必须是良构的。( (U*)0 ) - 返回值
-
.shared_ptr
( r, static_cast ::element_type*>(r.get()) )
警告
|
看似等效的表达式shared_ptr 最终会导致未定义的行为,试图删除同一个对象两次。 |
const_pointer_cast
template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
const_cast
必须是良构的。( (U*)0 ) - 返回值
-
.shared_ptr
( r, const_cast ::element_type*>(r.get()) )
dynamic_pointer_cast
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
dynamic_cast
必须是良构的。( (U*)0 ) - 返回值
-
-
当
dynamic_cast
返回非零值::element_type*>(r.get()) p
时,shared_ptr
;(r, p) -
否则,
shared_ptr
。()
-
reinterpret_pointer_cast
template<class T, class U>
shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
reinterpret_cast
必须是良构的。( (U*)0 ) - 返回值
-
.shared_ptr
( r, reinterpret_cast ::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);
-
- 效果
-
.os << p.get();
- 返回值
-
.os
get_deleter
template<class D, class T>
D * get_deleter(shared_ptr<T> const & p) noexcept;
-
- 返回值
-
如果
*this
拥有类型为(cv-unqualified)D
的删除器d
,则返回&d
;否则返回0。
原子访问
注意
|
本节中的函数对于由*p 标识的第一个shared_ptr 参数是原子的。如果仅通过本节中的函数进行操作,则对同一shared_ptr 实例的并发访问不是数据竞争。 |
template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;
-
- 返回值
-
.false
注意此实现不是无锁的。
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;
-
- 返回值
-
.*p
注意int
参数是memory_order
,但是此实现不使用它,因为它基于锁,因此始终是顺序一致的。
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;
-
- 效果
-
.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;
-
- 效果
-
.p->swap(r)
- 返回值
-
*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;
-
- 效果
-
如果
*p
等效于*v
,则将w
赋值给*p
,否则将*p
赋值给*v
。 - 返回值
-
如果
*p
等效于*v
则为true
,否则为false
。 - 备注
-
如果两个
shared_ptr
实例存储相同的指针值并共享所有权,则它们是等效的。
示例
有关完整的示例程序,请参见shared_ptr_example.cpp。该程序构建了一个std::vector
和一个std::set
的shared_ptr
对象。
请注意,在填充容器之后,一些shared_ptr
对象的引用计数将为1而不是2,因为集合是std::set
而不是std::multiset
,因此不包含重复项。此外,在执行push_back
和insert
容器操作时,引用计数甚至可能更高。更复杂的是,容器操作可能在各种情况下抛出异常。如果没有智能指针,正确处理此示例中的内存管理和异常处理将是一场噩梦。
句柄/主体惯用法
shared_ptr
的一个常见用法是实现句柄/主体(也称为pimpl)惯用法,该惯用法避免在头文件中公开主体(实现)。
示例程序shared_ptr_example2_test.cpp包含一个头文件shared_ptr_example2.hpp,它使用指向不完整类型的shared_ptr
来隐藏实现。需要完整类型的成员函数的实例化发生在shared_ptr_example2.cpp实现文件中。请注意,不需要显式析构函数。与~scoped_ptr
不同,~shared_ptr
不需要T
是一个完整类型。
线程安全
shared_ptr
对象提供与内置类型相同的线程安全级别。多个线程可以同时“读取”(仅使用const操作访问)一个shared_ptr
实例。多个线程可以同时“写入”(使用可变操作访问,例如operator=
或reset
)不同的shared_ptr
实例(即使这些实例是副本,并在底层共享相同的引用计数)。
任何其他同时访问都会导致未定义的行为。
示例
shared_ptr<int> p(new int(42));
shared_ptr
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
shared_ptr
实例// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
shared_ptr
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
shared_ptr
// 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
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
从Boost 1.33.0版本开始,shared_ptr
在大多数常见平台上使用无锁实现。
如果您的程序是单线程的,并且没有链接到可能在其默认配置中使用shared_ptr
的任何库,则可以在项目范围内#define
宏BOOST_SP_DISABLE_THREADS
以切换到普通的非原子引用计数更新。
(在某些(而不是全部)翻译单元中定义BOOST_SP_DISABLE_THREADS
在技术上违反了单定义规则,并且是未定义的行为。但是,实现试图尽力满足在这些翻译单元中使用非原子更新的要求。不过,没有保证。)
您可以定义宏BOOST_SP_USE_PTHREADS
来关闭无锁特定于平台的实现,并回退到基于通用pthread_mutex_t
的代码。
常见问题
-
共享指针有几种变体,各有不同的权衡;为什么智能指针库只提供一种实现?能够尝试每种类型以找到最适合当前工作的类型将很有用。
shared_ptr
的一个重要目标是提供标准的共享所有权指针。拥有单一指针类型对于稳定的库接口非常重要,因为不同的共享指针通常不能互操作,即引用计数指针(由库A使用)不能与链接指针(由库B使用)共享所有权。 -
为什么
shared_ptr
没有模板参数来提供特性或策略以允许广泛的用户自定义?参数化会劝退用户。
shared_ptr
模板经过精心设计,可在无需广泛参数化的情况下满足常见需求。 -
我不信服。在适当的情况下可以使用默认参数来隐藏复杂性。再说一次,为什么不用策略呢?
模板参数会影响类型。参见上面第一个问题的答案。
-
为什么
shared_ptr
不使用链表实现?链表实现带来的好处不足以抵消额外指针带来的成本。此外,使链表实现线程安全代价昂贵。
-
为什么
shared_ptr
(或其他任何Boost智能指针)不提供自动转换为T*?自动转换被认为容易出错。
-
为什么
shared_ptr
提供use_count()
?作为编写测试用例和调试显示的辅助手段。其中一个原型包含
use_count()
,它在追踪一个复杂的项目中的bug时非常有用,该项目最终存在循环依赖关系。 -
为什么
shared_ptr
不指定复杂度要求?因为复杂度要求会限制实现者,并在没有明显益处的情况下使规范复杂化。例如,如果必须满足严格的复杂度要求,错误检查实现可能会变得不符合规范。
-
为什么
shared_ptr
不提供release()
函数?除非
shared_ptr
是unique()
,否则它不能放弃所有权,因为另一个副本仍将销毁对象。考虑一下
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.
此外,
release()
返回的指针很难可靠地释放,因为源shared_ptr
可能是用自定义删除器创建的,或者可能指向不同类型的对象。 -
为什么
operator->()
是const的,但它的返回值是指向元素类型的非常量指针?浅拷贝指针,包括原始指针,通常不会传播const性。这样做几乎没有意义,因为你总是可以从const指针获得非常量指针,然后通过它修改对象。
shared_ptr
是“尽可能接近原始指针,但不会更近”。
weak_ptr:非拥有者观察者
描述
weak_ptr
类模板存储对由shared_ptr
管理的对象的“弱引用”。为了访问对象,可以使用带有weak_ptr
的shared_ptr
构造函数或weak_ptr
成员函数lock
将weak_ptr
转换为shared_ptr
。当指向对象的最后一个shared_ptr
消失且对象被删除时,尝试从引用已删除对象的weak_ptr
实例获取shared_ptr
将失败:构造函数将抛出类型为boost::bad_weak_ptr
的异常,而weak_ptr::lock
将返回一个空的shared_ptr
。
每个weak_ptr
都满足C++标准库的CopyConstructible
和Assignable
要求,因此可以在标准库容器中使用。提供比较运算符,以便weak_ptr
与标准库的关联容器一起工作。
weak_ptr
操作永不抛出异常。
类模板的参数化对象为T
,即指向的对象的类型。
与shared_ptr
相比,weak_ptr
提供了一组非常有限的操作,因为在多线程程序中访问其存储的指针通常很危险,有时甚至在单个线程内也不安全(即,它可能调用未定义的行为)。假设weak_ptr
有一个返回原始指针的get成员函数,并考虑这段简单的代码
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(int * r = q.get())
{
// use *r
}
想象一下,在if
之后,但在使用r
之前,另一个线程执行语句p.reset()
。现在r
是一个悬空指针。
解决这个问题的方法是从q
创建一个临时的shared_ptr
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(shared_ptr<int> r = q.lock())
{
// use *r
}
现在r
保存对q
指向的对象的引用。即使在另一个线程中执行p.reset()
,该对象也会一直存在,直到r
超出范围或被重置。通过获取对象的shared_ptr
,我们有效地阻止了它的销毁。
概要
weak_ptr
定义在<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;
}
成员
element_type
typedef ... element_type;
当T
不是数组类型时,element_type
为T
;当T
为U[]
或U[N]
时,element_type
为U
。
构造函数
weak_ptr() noexcept;
-
- 效果
-
构造一个空的
weak_ptr
。 - 后置条件
-
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;
-
- 效果
-
如果
r
为空,则构造一个空的weak_ptr
;否则,构造一个与r
共享所有权的weak_ptr
,就像存储r
中存储的指针的副本一样。 - 后置条件
-
.use_count() == r.use_count()
weak_ptr(weak_ptr && r) noexcept;
-
- 效果
-
构造一个值为
r
的weak_ptr
。 - 后置条件
-
r
为空。
别名构造函数
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;
- 效果
-
从
r
构造一个weak_ptr
,就像使用相应的转换/复制/移动构造函数一样,但是存储p
。 - 后置条件
-
use_count() == r.use_count()
。当!expired()
时,shared_ptr
。(*this).get() == p
注意
|
这些构造函数是扩展,在std::weak_ptr 中不存在。 |
析构函数
~weak_ptr() noexcept;
-
- 效果
-
销毁此
weak_ptr
,但对它存储的指针指向的对象没有影响。
赋值
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;
-
- 效果
-
相当于
weak_ptr(r).swap(*this)
。
注意实现可以自由地通过不同的方式满足效果(和隐含的保证),而无需创建临时对象。
use_count
long use_count() const noexcept;
-
- 返回值
-
如果
*this
为空,则为0;否则,与*this
共享所有权的shared_ptr
对象的个数。
expired
bool expired() const noexcept;
-
- 返回值
-
use_count() == 0
.
empty
bool empty() const noexcept;
-
- 返回值
-
当
*this
为空时为true
,否则为false
。
注意此函数是扩展,在 std::weak_ptr
中不存在。
lock
shared_ptr<T> lock() const noexcept;
-
- 返回值
-
expired()? shared_ptr
.(): shared_ptr (*this)
reset
void reset() noexcept;
-
- 效果
-
相当于
weak_ptr().swap(*this)
。
swap
void swap(weak_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
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;
-
- 返回值
-
参见
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;
-
- 返回值
-
当且仅当
*this
和r
共享所有权或都为空时为true
。
owner_hash_value
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;
-
- 返回值
-
一个未指定的值,使得
-
operator<
是C++标准[lib.alg.sorting]节中描述的严格弱排序; -
根据由
operator<
定义的等价关系!(a < b) && !(b < a)
,当且仅当两个weak_ptr
实例共享所有权或都为空时,它们才等价。
-
注意
|
允许weak_ptr 对象用作关联容器中的键。 |
swap
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
-
- 效果
-
相当于
a.swap(b)
。
常见问题
-
对象可以在其构造函数中创建对自身的弱指针吗?
不可以。
weak_ptr
只能从shared_ptr
创建,并且在对象构造时,尚不存在指向该对象的shared_ptr
。即使你可以创建一个指向this
的临时shared_ptr
,它也将在构造函数结束时超出范围,并且所有weak_ptr
实例都将立即过期。解决方案是将构造函数设为私有,并提供一个返回
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:创建 shared_ptr
描述
函数模板make_shared
和allocate_shared
提供了方便、安全和高效的方法来创建shared_ptr
对象。
基本原理
一致地使用shared_ptr
可以消除使用显式delete
的需要,但它本身并不支持避免显式new
。用户反复要求提供一个工厂函数,该函数创建一个给定类型的对象并返回一个指向它的shared_ptr
。除了方便性和风格之外,这样的函数也是异常安全的,并且速度更快,因为它可以使用单个分配来同时分配对象及其对应的控制块,从而消除了shared_ptr
构造开销的很大一部分。这消除了关于shared_ptr
的主要效率抱怨之一。
为了满足这一需求,提供了重载函数模板族make_shared
和allocate_shared
。make_shared
使用全局operator new
分配内存,而allocate_shared
使用用户提供的分配器,允许更精细的控制。
选择名称make_shared
的原因是表达式make_shared
可以朗读出来,并传达预期的含义。
最初,Boost函数模板allocate_shared
和make_shared
仅适用于标量对象。需要高效地分配数组对象。类模板shared_array
的一个批评总是缺乏像make_shared
这样的实用程序,它只使用一次分配。当shared_ptr
增强到支持数组类型时,为数组类型提供了allocate_shared
和make_shared
的其他重载。
概要
make_shared
和allocate_shared
定义在<boost/smart_ptr/make_shared.hpp>
中。
namespace boost {
// T is not an array
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 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);
}
通用需求
除非另有说明,否则适用于所有make_shared
和allocate_shared
重载的通用要求如下所述。
- 要求
-
A
应为分配器。A
的复制构造函数和析构函数不应抛出异常。 - 效果
-
为类型
T
的一个对象或n
个U
对象(如果T
是U[]
形式的数组类型,并且n
由参数确定,由具体的重载指定)分配内存。对象根据具体的重载指定的参数进行初始化。使用a
的重新绑定副本(对于未指定的value_type
)来分配内存。如果抛出异常,则函数无效。 - 返回值
-
一个
shared_ptr
实例,存储并拥有新构造对象的地址。 - 后置条件
-
r.get() != 0
且r.use_count() == 1
,其中r
是返回值。 - 抛出
-
std::bad_alloc
,从A::allocate
或对象的初始化抛出的异常。 - 备注
-
-
执行不超过一次内存分配。这提供了与侵入式智能指针等效的效率。
-
当指定将数组类型的对象初始化为相同类型
v
的值时,这应解释为将对象的每个数组元素初始化为v
中的相应元素。 -
当指定将数组类型的对象值初始化时,这应解释为将对象的每个数组元素值初始化。
-
当指定将非数组类型
U
的(子)对象初始化为值v
或从args...
构造时,make_shared
应通过表达式::new(p) U(expr)
执行此初始化(其中_expr_
分别是v
或std::forward
),并且(args)...) p
的类型为void*
,并指向适合容纳类型U
的对象的存储区。 -
当指定将非数组类型
U
的(子)对象初始化为值v
或从args...
构造时,allocate_shared
应通过表达式std::allocator_traits
执行此初始化(其中::construct(a2, p, expr) _expr_
分别是v
或std::forward
),(args)...) p
指向适合容纳类型U
的对象的存储区,并且类型为A2
的a2
是a
的潜在重新绑定副本。 -
当指定将非数组类型
U
的(子)对象默认初始化时,make_shared_noinit
和allocate_shared_noinit
应通过表达式::new(p) U
执行此初始化,其中p
的类型为void*
,并指向适合容纳类型U
的对象的存储区。 -
当指定将非数组类型
U
的(子)对象值初始化时,make_shared
应通过表达式::new(p) U()
执行此初始化,其中p
的类型为void*
,并指向适合容纳类型U
的对象的存储区。 -
当指定将非数组类型
U
的(子)对象值初始化时,allocate_shared
应通过表达式std::allocator_traits
执行此初始化,其中::construct(a2, p) p
指向适合容纳类型U
的对象的存储区,并且类型为A2
的a2
是a
的潜在重新绑定副本。 -
数组元素按其地址的升序初始化。
-
当返回值管理的对象的生命周期结束时,或当数组元素的初始化抛出异常时,应以与其构造相反的顺序销毁已初始化的元素。
-
注意
|
这些函数通常会分配比元素对象总大小更多的内存,以便为内部簿记结构(例如引用计数)留出空间。 |
自由函数
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
不是数组。 - 返回值
-
从
args...
构造的指向类型T
的对象的shared_ptr
。 - 示例
-
auto p = make_shared
(); -
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);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的n
个值初始化对象的序列的shared_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界已知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的extent_v<T>
个值初始化对象的序列的shared_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的n
个对象的序列的shared_ptr
,每个对象都初始化为v
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界已知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的extent_v<T>
个对象的序列的shared_ptr
,每个对象都初始化为v
。 - 示例
-
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);
-
- 约束条件
-
T
不是数组,或者是一个边界已知的数组。 - 返回值
-
一个指向类型为
T
的默认初始化对象的shared_ptr
,或者分别指向类型为remove_extent_t<T>
的extent_v<T>
个默认初始化对象的序列的shared_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的*n*
个默认初始化对象的序列的shared_ptr
。 - 示例
-
auto p = make_shared_noinit<double[]>(1024);
enable_shared_from_this
描述
类模板 enable_shared_from_this
用作基类,允许从成员函数中获取指向当前对象的 shared_ptr
或 weak_ptr
。
enable_shared_from_this<T>
定义了两个名为 shared_from_this
的成员函数,它们根据 constness 返回指向 this
的 shared_ptr<T>
和 shared_ptr<T const>
。它还定义了两个名为 weak_from_this
的成员函数,它们返回相应的 weak_ptr
。
示例
#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
}
概要
enable_shared_from_this
定义在 <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;
}
}
成员
enable_shared_from_this(enable_shared_from_this const &) noexcept;
-
- 效果
-
默认构造
weak_this_
。
注意weak_this_
*不会* 从参数复制。
enable_shared_from_this& operator=(enable_shared_from_this const &) noexcept;
-
- 返回值
-
.*this
注意weak_this_
保持不变。
template<class T> shared_ptr<T> shared_from_this();
template<class T> shared_ptr<T const> shared_from_this() const;
-
- 返回值
-
shared_ptr<T>(weak_this_)
.
注意当 *this
不由shared_ptr
拥有时,这些成员会抛出bad_weak_ptr
异常。
注意
|
当它由指向
|
template<class T> weak_ptr<T> weak_from_this() noexcept;
template<class T> weak_ptr<T const> weak_from_this() const noexcept;
-
- 返回值
-
weak_this_
.
注意与 shared_from_this()
不同,weak_from_this()
在析构函数中有效,并返回一个expired()
为 true 但仍然与其他引用该对象的weak_ptr
实例(如果有)共享所有权的weak_ptr
。
enable_shared_from
描述
enable_shared_from
用作基类,允许通过使用函数 shared_from
和 weak_from
,在给定指向对象的原始指针的情况下获取 shared_ptr
或 weak_ptr
。
enable_shared_from
与 enable_shared_from_this<T>
的区别在于它不是模板,并且是新代码的推荐替代方案。
示例
#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
}
概要
enable_shared_from
定义在 <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;
}
函数
template<class T> shared_ptr<T> shared_from( T * p );
-
- 返回值
-
shared_ptr<T>( p->enable_shared_from::shared_from_this(), p )
.
注意当 p
不由shared_ptr
拥有时,抛出bad_weak_ptr
异常。
template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
-
- 返回值
-
weak_ptr<T>( p->enable_shared_from::weak_from_this(), p )
.
注意与 shared_from(this)
不同,weak_from(this)
在析构函数中有效,并返回一个expired()
为 true 但仍然与其他引用该对象的weak_ptr
实例(如果有)共享所有权的weak_ptr
。
make_unique:创建 unique_ptr
描述
make_unique
函数模板提供了创建 std::unique_ptr
对象的便捷且安全的方法。
基本原理
C++11 标准引入了 std::unique_ptr
,但没有提供任何类似于 std::make_shared
的 make_unique
实用程序,后者提供了相同的异常安全性和避免编写 new
表达式的便利性。在一些标准库厂商实现它之前(以及在 C++14 标准引入 std::make_unique
之前),由于用户的请求,本库提供了它。
本库还为默认初始化提供了 make_unique
的额外重载,当用户不需要或不想承担值初始化的开销时。C++20 标准现在通过 std::make_unique_for_overwrite
提供了此功能。
概要
make_unique
定义在 <boost/smart_ptr/make_unique.hpp>
中。
namespace boost {
// T is not an array
template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);
// 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);
}
自由函数
template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);
-
- 约束条件
-
T
不是数组。 - 返回值
-
std::unique_ptr<T>(new T(std::forward<Args>(args)...)
. - 示例
-
auto p = make_unique<int>();
template<class T>
std::unique_ptr<T> make_unique(type_identity_t<T>&& v);
-
- 约束条件
-
T
不是数组。 - 返回值
-
std::unique_ptr<T>(new T(std::move(v))
. - 示例
-
auto p = make_unique<std::vector<int> >({1, 2});
template<class T>
std::unique_ptr<T> make_unique(std::size_t n);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
std::unique_ptr<T>(new remove_extent_t<T>[n]())
. - 示例
-
auto p = make_unique<double[]>(1024);
template<class T>
std::unique_ptr<T> make_unique_noinit();
-
- 约束条件
-
T
不是数组。 - 返回值
-
std::unique_ptr<T>(new T)
. - 示例
-
auto p = make_unique_noinit<std::array<double, 1024> >();
template<class T>
std::unique_ptr<T> make_unique_noinit(std::size_t n);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
std::unique_ptr<T>(new remove_extent_t<T>[n])
. - 示例
-
auto p = make_unique_noinit<double[]>(1024);
allocate_unique:创建 unique_ptr
描述
allocate_unique
函数模板系列提供了使用分配器创建管理新对象的 std::unique_ptr
的便捷且安全的方法。
基本原理
C++14 标准引入了使用 operator new
创建新对象的 std::make_unique
。但是,标准库中没有方便的工具可以使用分配器来创建 std::unique_ptr
管理的对象。编写分配器感知代码的用户经常请求 allocate_unique
工厂函数。此函数与 std::unique_ptr
的关系如同 std::allocate_shared
与 std::shared_ptr
的关系。
概要
allocate_unique
定义在 <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>>;
// T is not an array
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
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;
}
通用需求
除非另有说明,否则适用于所有 allocate_unique
和 allocate_unique_noinit
重载的常见要求如下所述。
- 要求
-
A
应为分配器。A
的复制构造函数和析构函数不应抛出异常。 - 效果
-
为类型
T
的一个对象或n
个U
对象(如果T
是U[]
形式的数组类型,并且n
由参数确定,由具体的重载指定)分配内存。对象根据具体的重载指定的参数进行初始化。使用a
的重新绑定副本(对于未指定的value_type
)来分配内存。如果抛出异常,则函数无效。 - 返回值
-
一个存储并拥有新构造对象地址的
std::unique_ptr
实例。 - 后置条件
-
r.get() != 0
,其中r
是返回值。 - 抛出
-
从
A::allocate
或从对象的初始化抛出的异常。 - 备注
-
-
当指定将数组类型的对象初始化为相同类型
v
的值时,这应解释为将对象的每个数组元素初始化为v
中的相应元素。 -
当指定将数组类型的对象值初始化时,这应解释为将对象的每个数组元素值初始化。
-
当指定非数组类型
U
的(子)对象初始化为值v
或从args...
构造时,allocate_unique
将通过表达式std::allocator_traits<A2>::construct(a2, p, expr)
执行此初始化(其中*expr*
分别为v
或std::forward<Args>(args)...)
),p
指向适合容纳类型为U
的对象的存储空间,并且类型为A2
的a2
是a
的潜在重新绑定副本。 -
当指定非数组类型
U
的(子)对象进行默认初始化时,allocate_unique_noinit
将通过表达式::new(p) U
执行此初始化,其中p
的类型为void*
并指向适合容纳类型为U
的对象的存储空间。 -
当指定非数组类型
U
的(子)对象进行值初始化时,allocate_unique
将通过表达式std::allocator_traits<A2>::construct(a2, p)
执行此初始化,其中p
指向适合容纳类型为U
的对象的存储空间,并且类型为A2
的a2
是a
的潜在重新绑定副本。 -
数组元素按其地址的升序初始化。
-
当返回值管理的对象的生命周期结束时,或当数组元素的初始化抛出异常时,应以与其构造相反的顺序销毁已初始化的元素。
-
自由函数
template<class T, class A, class... Args>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, Args&&... args);
-
- 约束条件
-
T
不是数组。 - 返回值
-
一个从
args...
构造的类型为T
的对象的std::unique_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
不是数组。 - 返回值
-
一个从
v
构造的类型为T
的对象的std::unique_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的n
个值初始化对象的序列的std::unique_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界已知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的extent_v<T>
个值初始化对象的序列的std::unique_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的n
个对象序列的std::unique_ptr
,每个对象都初始化为v
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界已知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的extent_v<T>
个对象序列的std::unique_ptr
,每个对象都初始化为v
。 - 示例
-
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);
-
- 约束条件
-
T
不是数组。 - 返回值
-
一个指向类型为
T
的默认初始化对象的std::unique_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界未知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的n
个默认初始化对象的序列的std::unique_ptr
。 - 示例
-
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);
-
- 约束条件
-
T
是一个边界已知的数组。 - 返回值
-
一个指向类型为
remove_extent_t<T>
的extent_v<T>
个默认初始化对象的序列的std::unique_ptr
。 - 示例
-
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;
-
- 返回值
-
指向分配的分配器指针。
- 示例
-
auto r = boost::get_allocator_ptr(p);
删除器
类模板 alloc_deleter
是 allocate_unique
函数使用的删除器。
概要
template<class T, class A>
class alloc_deleter {
public:
using pointer =
unspecified
;
explicit alloc_deleter(const A& a) noexcept;
void operator()(pointer p);
};
成员
using pointer =
unspecified
;
-
满足 *NullablePointer* 的类型。
explicit alloc_deleter(const A& a) noexcept;
-
- 效果
-
从
a
初始化存储的分配器。
void operator()(pointer p);
-
- 效果
-
使用存储的分配器销毁对象并释放
p
引用的存储空间。
intrusive_ptr:管理具有嵌入式计数的对象
描述
intrusive_ptr
类模板存储指向具有嵌入式引用计数的对象的指针。每个新的 intrusive_ptr
实例都会通过对函数 intrusive_ptr_add_ref
进行非限定调用(将指针作为参数传递)来递增引用计数。类似地,当 intrusive_ptr
被销毁时,它会调用 intrusive_ptr_release
;当引用计数降至零时,此函数负责销毁对象。用户应提供这两个函数的合适定义。在支持参数相关查找的编译器上,intrusive_ptr_add_ref
和 intrusive_ptr_release
应该在与其参数对应的命名空间中定义;否则,定义需要放在 boost
命名空间中。该库提供了一个辅助基类模板 intrusive_ref_counter,它可以帮助向用户类型添加对 intrusive_ptr
的支持。
类模板的参数是 T
,即指向的对象的类型。只要 T*
可以隐式转换为 U*
,intrusive_ptr<T>
就可以隐式转换为 intrusive_ptr<U>
。
使用 intrusive_ptr
的主要原因是:
-
一些现有的框架或操作系统提供具有嵌入式引用计数的对象;
-
intrusive_ptr
的内存占用与相应的原始指针相同; -
intrusive_ptr<T>
可以从类型为T*
的任意原始指针构造。
一般来说,如果intrusive_ptr
和shared_ptr
哪个更适合您的需求并不明显,请先尝试基于shared_ptr
的设计。
概要
intrusive_ptr
定义在<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);
}
成员
element_type
typedef T element_type;
提供模板参数T的类型。
构造函数
intrusive_ptr() noexcept;
-
- 后置条件
-
get() == 0
.
intrusive_ptr(T * p, bool add_ref = true);
-
- 效果
-
if(p != 0 && add_ref) intrusive_ptr_add_ref(p);
. - 后置条件
-
get() == p
.
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
-
- 效果
-
T * p = r.get(); if(p != 0) intrusive_ptr_add_ref(p);
. - 后置条件
-
get() == r.get()
.
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
-
- 后置条件
-
get()
等于r.get()
的旧值。r.get() == 0
。
析构函数
~intrusive_ptr();
-
- 效果
-
if(get() != 0) intrusive_ptr_release(get());
.
赋值
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(r).swap(*this)
。 - 返回值
-
.*this
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
-
- 效果
-
等效于
intrusive_ptr(std::move(r)).swap(*this)
。 - 返回值
-
.*this
reset
void reset();
-
- 效果
-
等效于
intrusive_ptr().swap(*this)
。
void reset(T * r);
-
- 效果
-
等效于
intrusive_ptr(r).swap(*this)
。
void reset(T * r, bool add_ref);
-
- 效果
-
等效于
intrusive_ptr(r, add_ref).swap(*this)
。
间接寻址
T & operator*() const noexcept;
-
- 需求
-
.get() != 0
- 返回值
-
.*get()
T * operator->() const noexcept;
-
- 需求
-
.get() != 0
- 返回值
-
.get()
get
T * get() const noexcept;
-
- 返回值
-
存储的指针。
detach
T * detach() noexcept;
-
- 返回值
-
存储的指针。
- 后置条件
-
get() == 0
.
注意
|
返回的指针具有更高的引用计数。这允许将intrusive_ptr 转换回原始指针,而无需获取和丢弃额外引用的性能开销。可以将其视为非引用递增构造函数的补充。 |
警告
|
使用detach 会逃脱intrusive_ptr 提供的自动引用计数的安全性。仅在绝对必要时(例如,与现有API接口时)才应使用它,并且在充分理解其含义的情况下使用。 |
转换
explicit operator bool () const noexcept;
-
- 返回值
-
.get() != 0
注意此转换运算符允许在布尔上下文中使用 intrusive_ptr
对象,例如if (p && p->valid()) {}
。
注意
|
在 C++03 编译器上,返回值的类型未指定。 |
swap
void swap(intrusive_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
自由函数
比较
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
-
- 返回值
-
.a.get() == b.get()
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
-
- 返回值
-
.a.get() != b.get()
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
-
- 返回值
-
a.get() == b
.
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
-
- 返回值
-
a.get() != b
.
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
-
- 返回值
-
a == b.get()
.
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
-
- 返回值
-
a != b.get()
.
template<class T>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
-
- 返回值
-
std::less<T *>()(a.get(), b.get())
.
注意允许将 intrusive_ptr
对象用作关联容器中的键。
swap
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
-
- 效果
-
相当于
a.swap(b)
。
get_pointer
template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
-
- 返回值
-
.p.get()
注意作为泛型编程的辅助工具提供。被 mem_fn
使用。
static_pointer_cast
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- 返回值
-
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;
-
- 返回值
-
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;
-
- 返回值
-
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);
-
- 效果
-
.os << p.get();
- 返回值
-
.os
intrusive_ref_counter
描述
intrusive_ref_counter
类模板实现了一个引用计数器,用于派生用户类,旨在与intrusive_ptr
一起使用。基类具有关联的intrusive_ptr_add_ref
和intrusive_ptr_release
函数,这些函数根据需要修改引用计数器,并在计数器降至零时销毁用户对象。
类模板的参数为Derived
和CounterPolicy
参数。第一个参数是派生自intrusive_ref_counter
的用户类。当没有剩余引用时,需要此类型才能正确销毁对象。
第二个参数是一个策略,用于定义引用计数器的性质。库提供了两个这样的策略:thread_unsafe_counter
和thread_safe_counter
。前者指示intrusive_ref_counter
基类仅使用适合单线程使用的计数器。使用这种引用计数器的单个对象的指针不得在不同的线程中使用。后一种策略使引用计数器线程安全,除非目标平台不支持线程。由于在现代系统中通常支持线程,因此默认计数器策略是thread_safe_counter
。
概要
intrusive_ref_counter
定义在<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;
}
成员
构造函数
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter&) noexcept;
-
- 后置条件
-
use_count() == 0
.
注意预期将指向已构造对象的指针传递给 intrusive_ptr
构造函数、赋值运算符或reset
方法,这将增加引用计数。
析构函数
~intrusive_ref_counter();
-
- 效果
-
销毁计数器对象。
注意析构函数是受保护的,因此只能通过 Derived
类销毁该对象。
赋值
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
-
- 效果
-
什么也不做,引用计数器不会被修改。
use_count
unsigned int use_count() const noexcept;
-
- 返回值
-
引用计数器的当前值。
注意在多线程应用程序中,返回值可能不准确。
自由函数
intrusive_ptr_add_ref
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- 效果
-
增加引用计数。
intrusive_ptr_release
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- 效果
-
减少引用计数。如果引用计数达到0,则调用
delete static_cast<const Derived*>(p)
。
local_shared_ptr:单线程内的共享所有权
描述
local_shared_ptr
几乎与shared_ptr
相同,唯一的区别是其引用计数使用非原子操作进行更新。因此,local_shared_ptr
及其所有副本必须驻留在(属于)单个线程中(因此得名)。
local_shared_ptr
可以转换为shared_ptr
,反之亦然。从shared_ptr
创建local_shared_ptr
会创建一个新的局部引用计数;这意味着两个local_shared_ptr
实例(都从同一个shared_ptr
创建)引用同一个对象,但共享同一个计数器,因此可以安全地由两个不同的线程使用。
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
但是,从第一个local_shared_ptr
创建第二个local_shared_ptr
会导致两者共享相同的计数。
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
从同一个local_shared_ptr
创建的两个shared_ptr
实例共享所有权。
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
这里p2.use_count()
是2,因为p1
也持有引用。
可以将local_shared_ptr<T>
视为shared_ptr<shared_ptr<T>>
,其中外部shared_ptr
使用非原子操作来计算其计数。从local_shared_ptr
转换为shared_ptr
会为您提供内部shared_ptr
的副本;从shared_ptr
转换会将其包装到具有非原子使用计数的外部shared_ptr
中(概念上来说),并返回结果。
概要
local_shared_ptr
定义在<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;
}
成员
element_type
typedef ... element_type;
当T
不是数组类型时,element_type
为T
;当T
为U[]
或U[N]
时,element_type
为U
。
默认构造函数
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
-
- 效果
-
构造一个空的
local_shared_ptr
。 - 后置条件
-
local_use_count() == 0 && get() == 0
.
指针构造函数
template<class Y> explicit local_shared_ptr(Y * p);
-
- 效果
-
构造一个拥有
shared_ptr<T>( p )
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1 && get() == p
. - 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。
带有析构器的构造函数
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
-
- 效果
-
构造一个拥有
shared_ptr<T>( p, d )
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1 && get() == p
. - 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。
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);
-
- 效果
-
构造一个拥有
shared_ptr<T>( p, d, a )
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1 && get() == p
. - 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。
复制构造函数和转换构造函数
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
如果
r
为空,则构造一个空的local_shared_ptr
;否则,构造一个与r
共享所有权的local_shared_ptr
。 - 后置条件
-
get() == r.get() && local_use_count() == r.local_use_count()
.
移动构造函数
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
从
r
移动构造一个local_shared_ptr
。 - 后置条件
-
*this
包含r
的旧值。r
为空且r.get() == 0
。
shared_ptr构造函数
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
-
- 效果
-
构造一个拥有
r
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1
。get()
返回r.get()
的旧值。 - 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。
别名构造函数
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
构造一个与
r
共享所有权并存储p
的local_shared_ptr
。 - 后置条件
-
get() == p && local_use_count() == r.local_use_count()
.
别名移动构造函数
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
从
r
移动构造一个local_shared_ptr
,同时存储p
。 - 后置条件
-
get() == p
并且local_use_count()
等于r
的旧计数。r
为空且r.get() == 0
。
unique_ptr
构造函数
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
-
- 要求
-
Y*
应该可以转换为T*
。 - 效果
-
-
当
r.get() == 0
时,等效于local_shared_ptr()
; -
否则,构造一个拥有
shared_ptr<T>( std::move(r) )
的local_shared_ptr
。
-
- 抛出
-
std::bad_alloc
,或者当无法获取内存以外的资源时抛出实现定义的异常。 - 异常安全性
-
如果抛出异常,则构造函数无效。
析构函数
~local_shared_ptr() noexcept;
-
- 效果
-
-
如果
*this
为空,或者与另一个local_shared_ptr
实例共享所有权(local_use_count() > 1
),则没有副作用。 -
否则,销毁拥有的
shared_ptr
。
-
赋值
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(r).swap(*this)
。 - 返回值
-
.*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);
-
- 效果
-
等效于
local_shared_ptr(std::move(r)).swap(*this)
。 - 返回值
-
.*this
local_shared_ptr & operator=(std::nullptr_t) noexcept;
-
- 效果
-
等效于
local_shared_ptr().swap(*this)
。 - 返回值
-
.*this
reset
void reset() noexcept;
-
- 效果
-
等效于
local_shared_ptr().swap(*this)
。
template<class Y> void reset(Y * p);
-
- 效果
-
等效于
local_shared_ptr(p).swap(*this)
。
template<class Y, class D> void reset(Y * p, D d);
-
- 效果
-
等效于
local_shared_ptr(p, d).swap(*this)
。
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- 效果
-
等效于
local_shared_ptr(p, d, a).swap(*this)
。
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
等效于
local_shared_ptr(r, p).swap(*this)
。
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
等效于
local_shared_ptr(std::move(r), p).swap(*this)
。
间接寻址
T & operator*() const noexcept;
-
- 要求
-
T
不能是数组类型。 - 返回值
-
.*get()
T * operator->() const noexcept;
-
- 要求
-
T
不能是数组类型。 - 返回值
-
.get()
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- 要求
-
T
应该是数组类型。存储的指针不能为0。i >= 0
。如果T
是U[N]
,则i < N
。 - 返回值
-
.get()[i]
get
element_type * get() const noexcept;
-
- 返回值
-
存储的指针。
local_use_count
long local_use_count() const noexcept;
-
- 返回值
-
与
*this
共享所有权的local_shared_ptr
对象数量(包括*this
),或者当*this
为空时为0。
转换
explicit operator bool() const noexcept;
-
- 返回值
-
.get() != 0
注意在 C++03 编译器上,返回值的类型未指定。
template<class Y> operator shared_ptr<Y>() const noexcept;
template<class Y> operator weak_ptr<Y>() const noexcept;
-
- 要求
-
T*
必须可转换为Y*
。 - 返回值
-
拥有的
shared_ptr
的副本。
swap
void swap(local_shared_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
owner_before
template<class Y> bool owner_before(local_shared_ptr<Y> const & r) const noexcept;
-
- 返回值
-
参见
operator<
的描述。
owner_equals
template<class Y> bool owner_equals(local_shared_ptr<Y> const & r) const noexcept;
-
- 返回值
-
当且仅当
*this
和r
共享所有权或都为空时为true
。
自由函数
比较
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;
-
- 返回值
-
.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;
-
- 返回值
-
.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;
-
- 返回值
-
.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;
-
- 返回值
-
.p.get() != 0
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 返回值
-
一个未指定的值,使得
-
operator<
是C++标准[lib.alg.sorting]节中描述的严格弱排序; -
根据由
operator<
定义的等价关系!(a < b) && !(b < a)
,当且仅当两个local_shared_ptr
实例共享所有权或两者都为空时,它们才等价。
-
注意
|
允许将local_shared_ptr 对象用作关联容器中的键。 |
注意
|
出于设计原因,省略了其余的比较运算符。 |
swap
template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
-
- 效果
-
相当于
a.swap(b)
。
get_pointer
template<class T>
typename local_shared_ptr<T>::element_type *
get_pointer(local_shared_ptr<T> const & p) noexcept;
-
- 返回值
-
.p.get()
注意作为泛型编程的辅助工具提供。被 mem_fn
使用。
static_pointer_cast
template<class T, class U>
local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
static_cast
必须是良构的。( (U*)0 ) - 返回值
-
local_shared_ptr<T>( r, static_cast<typename local_shared_ptr<T>::element_type*>(r.get()) )
.
警告
|
看似等效的表达式local_shared_ptr<T>(static_cast<T*>(r.get())) 最终会导致未定义的行为,尝试删除同一个对象两次。 |
const_pointer_cast
template<class T, class U>
local_shared_ptr<T> const_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
const_cast
必须是良构的。( (U*)0 ) - 返回值
-
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;
-
- 要求
-
表达式
dynamic_cast
必须是良构的。( (U*)0 ) - 返回值
-
-
当
dynamic_cast<typename local_shared_ptr<T>::element_type*>(r.get())
返回一个非零值p
时,local_shared_ptr<T>(r, p)
; -
否则,
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;
-
- 要求
-
表达式
reinterpret_cast
必须是良构的。( (U*)0 ) - 返回值
-
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);
-
- 效果
-
.os << p.get();
- 返回值
-
.os
get_deleter
template<class D, class T>
D * get_deleter(local_shared_ptr<T> const & p) noexcept;
-
- 返回值
-
如果
*this
拥有一个shared_ptr
实例p
,则为get_deleter<D>( p )
,否则为0。
make_local_shared:创建 local_shared_ptr
描述
函数模板make_local_shared
和allocate_local_shared
提供了创建local_shared_ptr
对象的便捷、安全和高效的方法。它们类似于shared_ptr
的make_shared
和allocate_shared
。
概要
make_local_shared
和allocate_local_shared
定义在<boost/smart_ptr/make_local_shared.hpp>
中。
namespace boost {
// T is not an array
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 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);
}
描述
这些函数的需求和效果与make_shared
和allocate_shared
相同,只是返回一个local_shared_ptr
。
泛型指针转换
描述
指针转换函数模板(static_pointer_cast
、dynamic_pointer_cast
、const_pointer_cast
和reinterpret_pointer_cast
)提供了一种为原始指针、std::shared_ptr
和std::unique_ptr
编写泛型指针转换的方法。
在pointer_cast_test.cpp中有测试和示例代码
基本原理
Boost智能指针通常重载这些函数以提供模拟指针转换的机制。例如,shared_ptr<T>
通过这种方式实现静态指针转换
template<class T, class U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
指针转换函数模板是针对原始指针、std::shared_ptr
和std::unique_ptr
的static_pointer_cast
、dynamic_pointer_cast
、const_pointer_cast
和reinterpret_pointer_cast
的重载。这样,在开发与指针类型无关的类(例如内存管理器或与共享内存兼容的类)时,可以对原始指针和智能指针使用相同的代码。
概要
泛型指针转换定义在<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;
}
自由函数
static_pointer_cast
template<class T, class U> T* static_pointer_cast(U* p) noexcept;
-
- 返回值
-
static_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
static_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回值
-
std::static_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
static_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
static_cast<T*>((U*)0)
必须格式良好。 - 返回值
-
std::unique_ptr<T>(static_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
警告
|
看似等效的表达式std::unique_ptr<T>(static_cast<T*>(p.get())) 最终会导致未定义的行为,尝试删除同一个对象两次。 |
dynamic_pointer_cast
template<class T, class U> T* dynamic_pointer_cast(U* p) noexcept;
-
- 返回值
-
dynamic_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
dynamic_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回值
-
std::dynamic_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
dynamic_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
static_cast<T*>((U*)0)
必须格式良好。 -
T
必须具有虚析构函数。- 返回值
-
当
dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.get())
返回一个非零值时,std::unique_ptr<T>(dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.release()));
。 -
否则,
std::unique_ptr<T>()
。
const_pointer_cast
template<class T, class U> T* const_pointer_cast(U* p) noexcept;
-
- 返回值
-
const_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
const_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回值
-
std::const_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
const_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
const_cast<T*>((U*)0)
必须格式良好。 - 返回值
-
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;
-
- 返回值
-
reinterpret_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
reinterpret_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回值
-
std::reinterpret_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
reinterpret_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
reinterpret_cast<T*>((U*)0)
必须格式良好。 - 返回值
-
std::unique_ptr<T>(reinterpret_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
示例
以下示例演示了泛型指针转换如何帮助我们创建指针无关代码。
#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
描述
pointer_to_other
实用程序提供了一种方法,给定一个源指针类型,可以获得指向另一个被指向类型相同类型的指针。
测试/示例代码位于 pointer_to_other_test.cpp。
基本原理
在构建指针无关类(如内存管理器、分配器或容器)时,通常需要泛型地定义指针,这样如果模板参数表示一个指针(例如,指向int
的原始指针或智能指针),我们可以定义另一个指向另一个被指向类型(指向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;
};
概要
pointer_to_other
定义在<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;
};
}
如果这些定义对于特定智能指针不正确,我们可以定义pointer_to_other
的特化。
示例
// 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;
};
正如我们所看到的,使用pointer_to_other
,我们可以创建指针无关代码。
atomic_shared_ptr
描述
类模板atomic_shared_ptr<T>
为类型为shared_ptr<T>
的包含值实现了std::atomic
的接口。对atomic_shared_ptr
的并发访问不是数据竞争。
概要
atomic_shared_ptr
定义在<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;
};
}
成员
constexpr atomic_shared_ptr() noexcept;
-
- 效果
-
默认初始化
p_
。
atomic_shared_ptr( shared_ptr<T> p ) noexcept;
-
- 效果
-
将
p_
初始化为p
。
atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;
-
- 效果
-
p_.swap(r)
. - 返回值
-
.*this
bool is_lock_free() const noexcept;
-
- 返回值
-
.false
注意此实现不是无锁的。
shared_ptr<T> load( int = 0 ) const noexcept;
operator shared_ptr<T>() const noexcept;
-
- 返回值
-
p_
.
注意int
参数旨在为memory_order
类型,但被忽略。此实现基于锁,因此始终顺序一致。
void store( shared_ptr<T> r, int = 0 ) noexcept;
-
- 效果
-
p_.swap(r)
.
shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;
-
- 效果
-
p_.swap(r)
. - 返回值
-
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;
-
- 效果
-
如果
p_
等于v
,则将w
赋值给p_
,否则将p_
赋值给v
。 - 返回值
-
如果
p_
等于v
,则为true
,否则为false
。 - 备注
-
如果两个
shared_ptr
实例存储相同的指针值并共享所有权,则它们是等效的。
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;
-
- 效果
-
如果
p_
等于v
,则将std::move(w)
赋值给p_
,否则将p_
赋值给v
。 - 返回值
-
如果
p_
等于v
,则为true
,否则为false
。 - 备注
-
无论哪种情况,
w
的旧值都不会保留。
owner_less
描述
owner_less<T>
是一个辅助函数对象,使用owner_before
比较两个智能指针对象。它仅为了与 C++11 保持兼容而提供,对应于同名标准组件。
使用 Boost 智能指针时,无需使用owner_less
,因为提供的operator<
重载(以及相应的std::less
)返回相同的结果。
概要
owner_less
定义在<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;
};
}
成员
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
-
- 返回值
-
u.owner_before( v )
.
owner_equal_to
描述
owner_equal_to<T>
是一个辅助函数对象,使用owner_equals
比较两个智能指针对象。
概要
owner_equal_to
定义在<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;
};
}
成员
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
-
- 返回值
-
u.owner_equals( v )
.
owner_hash
描述
owner_hash<T>
是一个辅助函数对象,它接受一个智能指针p
并返回p.owner_hash_value()
。它对于创建使用基于所有权的相等性(而不是默认的指针值相等性)的shared_ptr
的无序容器很有用。(它也可以与weak_ptr
一起使用,但没有必要,因为boost::hash
和std::hash
对于weak_ptr
已经使用了基于所有权的相等性。)
示例
std::unordered_set< boost::shared_ptr<void>,
boost::owner_hash< boost::shared_ptr<void> >,
boost::owner_equal_to< boost::shared_ptr<void> > > set;
概要
owner_hash
定义在<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;
};
}
成员
std::size_t operator()( T const & p ) const noexcept;
-
- 返回值
-
p.owner_hash_value()
.
附录 A:智能指针编程技巧
使用不完整类进行实现隐藏
一种行之有效的技术(在 C 中也能使用)用于将接口与实现分离,是使用指向不完整类的指针作为不透明句柄。
class FILE;
FILE * fopen(char const * name, char const * mode);
void fread(FILE * f, void * data, size_t size);
void fclose(FILE * f);
可以使用shared_ptr
来表达上述接口,从而无需手动调用fclose
。
class FILE;
shared_ptr<FILE> fopen(char const * name, char const * mode);
void fread(shared_ptr<FILE> f, void * data, size_t size);
此技术依赖于shared_ptr
执行自定义删除器、消除对fclose
的显式调用以及shared_ptr<X>
在X
不完整时可以复制和销毁的事实。
“Pimpl”惯用法
不完整类模式的 C++ 特定变体是“Pimpl”惯用法。不完整类不会暴露给用户;它隐藏在一个转发外观后面。shared_ptr
可用于实现“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);
}
这里需要注意的关键一点是,编译器生成的复制构造函数、赋值运算符和析构函数都具有合理的意义。因此,file
是CopyConstructible
和Assignable
,允许它在标准容器中使用。
使用抽象类进行实现隐藏
另一种广泛使用的 C++ 惯用法用于分离接口和实现,是使用抽象基类和工厂函数。抽象类有时称为“接口”,该模式称为“基于接口的编程”。同样,shared_ptr
可用作工厂函数的返回类型。
// 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;
}
shared_ptr
的一个关键属性是,分配、构造、释放和销毁细节在构造时,在工厂函数内部捕获。
请注意上面示例中的受保护和非虚析构函数。客户端代码不能也不需要删除指向X
的指针;从createX
返回的shared_ptr<X>
实例将正确调用~X_impl
。
防止delete px.get()
通常希望阻止客户端代码删除由shared_ptr
管理的指针。前面的技术展示了一种可能的方法,使用受保护的析构函数。另一种方法是使用私有删除器。
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;
}
};
封装分配细节,包装工厂函数
shared_ptr
可用于创建 C++ 包装器,用于现有 C 风格库接口,这些接口从其工厂函数返回原始指针以封装分配细节。例如,考虑此接口,其中CreateX
可能会从其自己的私有堆分配X
,~X
可能无法访问,或者X
可能不完整。
X * CreateX(); void DestroyX(X *);
可靠地销毁CreateX
返回的指针的唯一方法是调用DestroyX
。
以下是基于shared_ptr
的包装器可能的样子。
shared_ptr<X> createX() { shared_ptr<X> px(CreateX(), DestroyX); return px; }
调用createX
的客户端代码仍然不需要知道对象是如何分配的,但现在销毁是自动的。
使用 shared_ptr 保存指向静态分配对象的指针
有时需要为一个已经存在的对象创建一个shared_ptr
,以便当没有更多引用剩余时,shared_ptr
不会尝试销毁该对象。例如,工厂函数
shared_ptr<X> createX();
在某些情况下可能需要返回指向静态分配的X
实例的指针。
解决方案是使用什么也不做的自定义删除器。
struct null_deleter
{
void operator()(void const *) const
{
}
};
static X x;
shared_ptr<X> createX()
{
shared_ptr<X> px(&x, null_deleter());
return px;
}
同样的技术适用于任何已知比指针生命周期长的对象。
使用 shared_ptr 保存指向 COM 对象的指针
背景:COM 对象具有嵌入式引用计数和两个操作它的成员函数。AddRef()
增加计数。Release()
递减计数,当计数降为零时销毁自身。
可以在shared_ptr
中保存指向 COM 对象的指针。
shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p) { p->AddRef(); shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release)); return pw; }
但是,请注意,从pw
创建的shared_ptr
复制不会在 COM 对象的嵌入式计数中“注册”;它们将共享在make_shared_from_COM
中创建的单个引用。从pw
创建的弱指针在最后一个shared_ptr
被销毁时将失效,无论 COM 对象本身是否仍然存活。
如mem_fn
文档中所述,你需要首先#define BOOST_MEM_FN_ENABLE_STDCALL
。
使用 shared_ptr 保存指向具有嵌入式引用计数的对象的指针
这是上述技术的概括。该示例假设该对象实现了intrusive_ptr
所需的两个函数,intrusive_ptr_add_ref
和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;
}
使用 shared_ptr 保存另一个共享所有权智能指针
shared_ptr
的设计目标之一是在库接口中使用。可能会遇到库接受shared_ptr
参数的情况,但手头的对象由不同的引用计数或链接智能指针管理。
可以使用shared_ptr
的自定义删除器功能将此现有智能指针包装在shared_ptr
外观后面。
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;
}
一个微妙的点是删除器不允许抛出异常,并且上面编写的示例假设p_.reset()
不抛出异常。如果不是这种情况,则应将p_.reset();
包装在忽略异常的try {} catch(…) {}
块中。在(通常不太可能)抛出并忽略异常的情况下,p_
将在删除器的生命周期结束时被释放。当所有引用(包括弱指针)都被销毁或重置时,就会发生这种情况。
另一个巧妙之处在于,给定上述shared_ptr
实例,可以使用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
}
}
从原始指针获取 shared_ptr
有时需要获得一个shared_ptr
,给定一个指向已由另一个shared_ptr
实例管理的对象的原始指针。示例
void f(X * p) { shared_ptr<X> px(???); }
在f
内部,我们想创建一个指向*p
的shared_ptr
。
一般情况下,这个问题没有解决方案。一种方法是如果可能的话,修改f
以接受shared_ptr
。
void f(shared_ptr<X> px);
相同的转换可用于非虚拟成员函数,以转换隐式的this
。
void X::f(int m);
将成为具有shared_ptr
第一个参数的自由函数。
void f(shared_ptr<X> this_, int m);
如果无法更改f
,但X
使用侵入式计数,请使用上面描述的make_shared_from_intrusive
。或者,如果已知在f
中创建的shared_ptr
永远不会比对象存活时间长,则使用空删除器。
在构造函数中获取指向 this 的 shared_ptr (weak_ptr)
某些设计要求对象在构造时使用中央机构自行注册。当注册例程采用shared_ptr
时,这会导致如何才能让构造函数获得指向this
的shared_ptr
的问题。
class X
{
public:
X()
{
shared_ptr<X> this_(???);
}
};
一般情况下,这个问题无法解决。正在构造的X
实例可以是自动变量或静态变量;它可以在堆上创建。
shared_ptr<X> px(new X);
但在构造时,px
还不存在,并且不可能创建另一个与它共享所有权的shared_ptr
实例。
根据上下文,如果内部shared_ptr this_
不需要保持对象存活,请使用此处和此处解释的null_deleter
。如果X
应该始终存活在堆上,并由shared_ptr
管理,请使用静态工厂函数。
class X
{
private:
X() { ... }
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X);
// use px as 'this_'
return px;
}
};
获取指向 this 的 shared_ptr
有时需要在虚拟成员函数中从this
获取shared_ptr
,假设this
已由shared_ptr
管理。前面技术中描述的转换 无法应用。
一个典型的例子
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;
}
};
解决方案是在impl
中将指向this
的弱指针作为成员保留。
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;
}
};
该库现在包含一个辅助类模板enable_shared_from_this
,可用于封装解决方案。
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();
}
}
请注意,您不再需要手动初始化enable_shared_from_this
中的weak_ptr
成员。构造指向impl
的shared_ptr
会处理这个问题。
使用 shared_ptr 作为智能计数句柄
一些库接口使用不透明句柄,这是上面描述的不完整类技术 的一种变体。一个例子
typedef void * HANDLE;
HANDLE CreateProcess();
void CloseHandle(HANDLE);
可以使用shared_ptr
作为句柄,而不是原始指针,并免费获得引用计数和自动资源管理。
typedef shared_ptr<void> handle;
handle createProcess()
{
shared_ptr<void> pv(CreateProcess(), CloseHandle);
return pv;
}
使用 shared_ptr 在块退出时执行代码
shared_ptr<void>
可以在控制离开作用域时自动执行清理代码。
-
执行
f(p)
,其中p
是一个指针。shared_ptr<void> guard(p, f);
-
执行任意代码:
f(x, y)
。shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y));
使用 shared_ptr 保存任意对象
shared_ptr<void>
可以充当类似于void*
的通用对象指针。当构造为
shared_ptr<void> pv(new X);
的shared_ptr<void>
实例被销毁时,它将通过执行~X
正确地处理X
对象。
此属性的使用方式与使用原始void*
从对象指针中暂时剥离类型信息的方式大致相同。shared_ptr<void>
稍后可以使用static_pointer_cast
转换回正确的类型。
将任意数据与异构 shared_ptr 实例关联
shared_ptr
和 weak_ptr
支持标准关联容器(例如 std::map
)所需的 operator<
比较运算符。这可以用于非侵入式地将任意数据与由 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;
使用 shared_ptr 作为可复制互斥锁
有时需要从函数返回互斥锁,而不可复制的锁无法按值返回。可以使用 shared_ptr
作为互斥锁。
class mutex
{
public:
void lock();
void unlock();
};
shared_ptr<mutex> lock(mutex & m)
{
m.lock();
return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}
更好的是,充当锁的 shared_ptr
实例可以封装在一个专用的 shared_lock
类中。
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
现在可以这样使用:
shared_lock lock(m);
请注意,由于 shared_ptr<void>
能够隐藏类型信息,因此 shared_lock
不是根据互斥锁类型进行模板化的。
使用 shared_ptr 包装成员函数调用
shared_ptr
实现 Bjarne Stroustrup 的文章“Wrapping C++ Member Function Calls”(可在网上找到:[http://www.stroustrup.com/wrapper.pdf](http://www.stroustrup.com/wrapper.pdf))中描述的 Wrap/CallProxy
方案所需的拥有权语义。下面给出一个实现。
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();
}
延迟释放
在某些情况下,单个 px.reset()
可能会在性能关键区域触发代价高昂的释放操作。
class X; // ~X is expensive
class Y
{
shared_ptr<X> px;
public:
void f()
{
px.reset();
}
};
解决方案是通过将 px
移动到一个专用的空闲列表中来推迟潜在的释放操作,该空闲列表可以在性能和响应时间不是问题时定期清空。
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
另一种方法是通过使用延迟删除器将空闲列表逻辑移动到构造点。
struct delayed_deleter
{
template<class T> void operator()(T * p)
{
try
{
shared_ptr<void> pv(p);
free_list.push_back(pv);
}
catch(...)
{
}
}
};
指向未由 shared_ptr 管理的对象的弱指针
使对象持有指向自身的 shared_ptr
,使用 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_; }
};
当对象的生存期结束时,X::this_
将被销毁,所有弱指针将自动过期。
附录 B:历史和致谢
1994 年夏季
Greg Colvin [建议](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1994/N0555.pdf) C++ 标准委员会采用名为 auto_ptr
和 counted_ptr
的类,它们与我们现在称之为 scoped_ptr
和 shared_ptr
的类非常相似。在库工作组的建议未被全体委员会采纳的极少数情况下之一,counted_ptr
被拒绝,并且向 auto_ptr
添加了令人惊讶的拥有权转移语义。
1998 年 10 月
Beman Dawes 建议在 Per Andersson、Matt Austern、Greg Colvin、Sean Corfield、Pete Becker、Nico Josuttis、Dietmar Kühl、Nathan Myers、Chichiang Wan 和 Judy Ward 的会议上,使用 safe_ptr
和 counted_ptr
的名称恢复原始语义。在讨论期间,最终确定了四个新的类名,决定不需要完全遵循 std::auto_ptr
接口,并且最终确定了各种函数签名和语义。
在接下来的三个月中,考虑了 shared_ptr
的几种实现方案,并在 [boost.org](/) 邮件列表中进行了讨论。实现问题围绕着必须保留的引用计数展开,它可以附加到指向的对象上,也可以在其他地方分离。这些变体中的每一个本身都有两个主要变体。
-
直接分离:
shared_ptr
包含指向对象的指针和指向计数的指针。 -
间接分离:
shared_ptr
包含指向辅助对象的指针,该辅助对象又包含指向对象和计数的指针。 -
嵌入式附加:计数是所指向对象的成员。
-
位置附加:计数通过 operator new 操作附加。
每种实现技术都有其优点和缺点。我们甚至对直接和间接方法进行了各种计时测试,发现至少在英特尔奔腾芯片上,几乎没有可测量的差异。Kevlin Henney 提供了他撰写的关于“计数体技术”的论文。Dietmar Kühl 建议了一种优雅的部分模板特化技术,允许用户选择他们喜欢的实现,并且也对此进行了实验。
但 Greg Colvin 和 Jerry Schwarz 认为“参数化会劝退用户”,最终我们只选择提供直接实现。
1999 年 5 月
1999 年 4 月和 5 月,Valentin Bonnard 和 David Abrahams 提出了许多建议,导致了许多改进。
1999 年 9 月
Luis Coelho 提供了 shared_ptr::swap
和 shared_array::swap
。
1999 年 11 月
Darin Adler 提供了 operator ==
、operator !=
和 std::swap
,以及共享类型的 std::less
特化。
2001 年 5 月
Vladimir Prus 建议在销毁时需要一个完整类型。改进是在包括 Dave Abrahams、Greg Colvin、Beman Dawes、Rainer Deyke、Peter Dimov、John Maddock、Vladimir Prus、Shankar Sai 和其他人在内的讨论中发展起来的。
2002 年 1 月
Peter Dimov 重做了所有四个类,添加了功能,修复了错误,将它们拆分为四个单独的头文件,并添加了 weak_ptr
。
2003 年 3 月
Peter Dimov、Beman Dawes 和 Greg Colvin [建议](http://open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html) 通过第一个库技术报告(称为 TR1)将 shared_ptr
和 weak_ptr
包含到标准库中。该提案被接受,并最终成为 2011 年 C++ 标准的一部分。
2007 年 7 月
Peter Dimov 和 Beman Dawes [建议](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2351.htm) 对 shared_ptr
进行了一些增强,因为它进入了最终成为 C++11 标准的工作文档。
2012 年 11 月
Glen Fernandes 为数组提供了 make_shared
和 allocate_shared
的实现。它们为可以初始化构造函数参数或初始化列表的数组实现单次分配,以及用于默认初始化和无值初始化的重载。
Peter Dimov 通过扩展 shared_ptr
以支持 shared_ptr<T[]>
和 shared_ptr<T[N]>
语法来辅助此开发。
2013 年 4 月
Peter Dimov [建议](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3640.html) 将 shared_ptr
的扩展以支持数组包含到标准中,并且它被接受了。
2014 年 2 月
Glen Fernandes 更新了 make_shared
和 allocate_shared
以符合 C++ 标准论文 [N3870](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3870.html) 中的规范,并为数组和对象实现了 make_unique
。
Peter Dimov 和 Glen Fernandes 分别更新了标量和数组实现,以解决 C++ 标准库缺陷 2070。
2017 年 2 月
Glen Fernandes 重写了数组的 allocate_shared
和 make_shared
,以实现更优化和更易维护的实现。
2017 年 6 月
Peter Dimov 和 Glen Fernandes 使用 Asciidoc 格式重写了文档。
Peter Dimov 添加了 atomic_shared_ptr
和 local_shared_ptr
。
2019 年 8 月
Glen Fernandes 为标量和数组实现了 allocate_unique
。
附录 C:shared_array(已弃用)
注意
|
此功能已弃用,因为现在可以使用 shared_ptr 指向 T[] 或 T[N] ,并且在各个方面都更好。 |
描述
shared_array
类模板存储指向动态分配数组的指针。(动态分配的数组是用 C++ new[]
表达式分配的。)当指向它的最后一个 shared_array
被销毁或重置时,保证会删除指向的对象。
每个 shared_array
都满足 C++ 标准库的 *CopyConstructible* 和 *Assignable* 要求,因此可以在标准库容器中使用。提供比较运算符,以便 shared_array
可以与标准库的关联容器一起使用。
通常,shared_array
无法正确保存指向已使用非数组形式的 new
分配的对象的指针。有关此用法的说明,请参阅 shared_ptr
。
由于实现使用了引用计数,因此不会回收 shared_array
实例的循环。例如,如果 main
保存指向 A
的 shared_array
,而 A
直接或间接保存指向 A
的 shared_array
,则 A
的使用计数将为 2。原始 shared_array
的销毁将使 A
悬空,使用计数为 1。
指向 std::vector
的 shared_ptr
是 shared_array
的替代方案,它功能更强大,但灵活性更高。
类模板的参数是 T
,即指向的对象的类型。shared_array
及其大多数成员函数对 T
没有要求;它可以是不完整类型或 void
。对 T
有附加要求的成员函数(构造函数、reset)在下面有明确说明。
概要
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;
}
成员
element_type
typedef T element_type;
- 类型
-
提供存储指针的类型。
构造函数
explicit shared_array(T* p = 0);
-
- 效果
-
构造一个
shared_array
,存储p
的副本,p
必须是指向通过 C++new[]
表达式分配的数组的指针或为 0。之后,使用计数为 1(即使p == 0
;参见~shared_array
)。 - 要求
-
T
是完整类型。 - 抛出
-
std::bad_alloc
。如果抛出异常,则调用delete[] p
。
template<class D> shared_array(T* p, D d);
-
- 效果
-
构造一个
shared_array
,存储p
和d
的副本。之后,使用计数为 1。当需要删除p
指向的数组时,在语句d(p)
中使用对象d
。 - 要求
-
T
是完整类型。 -
D
的复制构造函数和析构函数不能抛出异常。 -
使用参数
p
调用对象d
不能抛出异常。- 抛出
-
std::bad_alloc
。如果抛出异常,则调用d(p)
。
shared_array(const shared_array& v) noexcept;
-
- 效果
-
构造一个
shared_array
,就像存储v
中存储的指针的副本一样。之后,所有副本的使用计数都比初始使用计数多 1。 - 要求
-
T
是完整类型。
析构函数
~shared_array() noexcept;
-
- 效果
-
递减使用计数。然后,如果使用计数为 0,则删除存储的指针指向的数组。请注意,对值为 0 的指针进行
delete[]
操作是无害的。
赋值
shared_array& operator=(const shared_array& v) noexcept;
-
- 效果
-
构造一个如上所述的新
shared_array
,然后用新shared_array
替换此shared_array
,销毁被替换的对象。 - 要求
-
T
是完整类型。 - 返回值
-
.*this
reset
void reset(T* p = 0);
-
- 效果
-
构造一个如上所述的新
shared_array
,然后用新shared_array
替换此shared_array
,销毁被替换的对象。 - 要求
-
T
是完整类型。 - 抛出
-
std::bad_alloc
。如果抛出异常,则调用delete[] p
。
template<class D> void reset(T* p, D d);
-
- 效果
-
构造一个如上所述的新
shared_array
,然后用新shared_array
替换此shared_array
,销毁被替换的对象。 - 要求
-
T
是完整类型。 -
D
的复制构造函数不能抛出异常。- 抛出
-
std::bad_alloc
。如果抛出异常,则调用d(p)
。
索引
T& operator[](std::ptrdiff_t n) const noexcept;
- 返回值
-
对存储的指针指向的数组的元素
n
的引用。如果存储的指针为 0,或者n
小于 0 或大于等于数组中的元素数,则行为未定义,几乎肯定是不希望的。 - 要求
-
T
是完整类型。
get
T* get() const noexcept;
-
- 返回值
-
存储的指针。
唯一性
bool unique() const noexcept;
-
- 返回值
-
如果没有任何其他
shared_array
共享存储指针的所有权,则为true
,否则为false
。
use_count
long use_count() const noexcept;
-
- 返回值
-
共享存储指针所有权的
shared_array
对象的数量。
转换
explicit operator bool() const noexcept;
-
- 返回值
-
.get() != 0
- 要求
-
T
是完整类型。
swap
void swap(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> bool
operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
-
- 返回值
-
比较两个智能指针的存储指针的结果。
注意
|
提供operator< 重载是为了定义排序规则,以便shared_array 对象可以用于关联容器,例如std::map 。实现使用std::less<T*> 进行比较。这确保了比较能够正确处理,因为标准规定指针上的关系运算符是未指定的(5.9 [expr.rel] 第2段),但指针上的std::less 是明确定义的(20.3.3 [lib.comparisons] 第8段)。 |
swap
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
-
- 返回值
-
a.swap(b)
. - 要求
-
T
是完整类型。
附录 D:版权和许可
本文档
-
版权所有 1999 Greg Colvin
-
版权所有 1999 Beman Dawes
-
版权所有 2002 Darin Adler
-
版权所有 2003-2020 Peter Dimov
-
版权所有 2005, 2006 Ion Gaztañaga
-
版权所有 2008 Frank Mori Hess
-
版权所有 2012-2017 Glen Fernandes
-
版权所有 2013 Andrey Semashev