介绍
智能指针是存储指向动态分配(堆)对象的指针的对象。它们的工作方式与内置 C++ 指针非常相似,但它们会在适当的时候自动删除所指向的对象。智能指针在面对异常时特别有用,因为它们可以确保动态分配对象的正确析构。它们还可以用于跟踪由多个所有者共享的动态分配对象。
从概念上讲,智能指针被视为拥有所指向的对象,因此负责在不再需要该对象时删除它。因此,它们是 Bjarne Stroustrup 的“C++ 程序设计语言”第三版第 14.4 节“资源管理”中描述的“资源获取即初始化”惯用法的示例。
此库提供了六个智能指针类模板
-
scoped_ptr,用于将动态分配对象的拥有权限定在当前作用域; -
scoped_array,为动态分配的数组提供作用域所有权; -
shared_ptr,管理对象或数组共享所有权的多功能工具; -
weak_ptr,对shared_ptr管理的对象进行非拥有观察,可以暂时提升为shared_ptr; -
intrusive_ptr,指向具有嵌入式引用计数对象的指针; -
local_shared_ptr,提供单线程内的共享所有权。
shared_ptr 和 weak_ptr 自 C++11 标准开始已成为标准的一部分。
此外,该库还包含以下支持性工具函数和类
-
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,一个用于将一种智能指针类型转换为另一种的辅助 trait; -
static_pointer_cast及其同类函数,通用智能指针转换; -
intrusive_ref_counter,一个包含引用计数的辅助基类。 -
atomic_shared_ptr,一个实现std::atomic接口的辅助类,用于shared_ptr类型的值。
总的来说,库中指针管理对象的析构函数或 operator delete 不允许抛出异常。
修订历史
1.90.0 版本更新
-
已移除通过已弃用的宏
BOOST_SP_ENABLE_DEBUG_HOOKS、BOOST_SP_USE_STD_ALLOCATOR和BOOST_SP_USE_QUICK_ALLOCATOR启用的功能。 -
头文件
<boost/smart_ptr/detail/quick_allocator.hpp>已标记为弃用,将在未来版本中移除。 -
不再支持定义
BOOST_NO_CXX11_HDR_ATOMIC的配置;现在需要符合 C++11 标准的<atomic>。 -
已弃用的宏
BOOST_AC_USE_SPINLOCK、BOOST_AC_USE_PTHREADS、BOOST_SP_USE_SPINLOCK和BOOST_SP_USE_PTHREADS不再起作用。 -
已移除平台特定的
atomic_count、sp_counted_base和spinlock实现,它们不再被使用。 -
不再支持定义
BOOST_NO_CXX11_HDR_MUTEX的配置;现在需要符合 C++11 标准的<mutex>。 -
已移除
boost/smart_ptr/detail/中一些未使用的头文件。
1.88.0 版中的更改
-
修复了宽字符流的
operator<<(在 1.87.0 版本中意外损坏)
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<T>::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 时 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 编译器上,返回值是未指定类型的。 |
交换
void swap(scoped_ptr & b) noexcept;
交换两个智能指针的内容。T 不必是完整类型。
自由函数
交换
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。事实上,一些人反对将 bool 包含在 C++ 标准中,就是因为这一点。但是,通过编写 bool 而非 int,您是在告诉读者您的意图。scoped_ptr 也是如此;通过使用它,您是在表达意图。
有人建议 scoped_ptr<T> 等同于 std::auto_ptr<T> const。然而,Ed Brey 指出,reset 对 std::auto_ptr<T> const 将不起作用。
句柄/体(Handle/Body)惯用法
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 编译器上,返回值是未指定类型的。 |
交换
void swap(scoped_array & b) noexcept;
交换两个智能指针的内容。T 不必是完整类型。
自由函数
交换
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 被销毁或重置时,它将调用 delete 来删除传递给构造函数的原始 int*,即使 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` 将导致 A 悬空,使用计数为 1。使用 weak_ptr 来“打破循环”。
该类模板以 T(所指向对象的类型)作为参数。shared_ptr 及其大多数成员函数对 T 没有要求;允许它为不完整类型或 void。对 T 有额外要求的成员函数(构造函数、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 release 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 临时变量来节省打字;考虑以下示例以了解为什么这很危险
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和一个调用delete[] p的删除器的shared_ptr。 - 后置条件
-
use_count() == 1 && get() == p。如果T不是数组类型且p可无歧义地转换为enable_shared_from_this<V>*(对于某个V),则p->shared_from_this()返回*this的副本。 - 抛出
-
std::bad_alloc,或在无法获得内存以外的资源时抛出的实现定义的异常。 - 异常安全
-
如果抛出异常,构造函数将调用
delete[] p(当T是数组类型时)或delete p(当T不是数组类型时)。
|
注意
|
p 必须是指向通过 C++ new 表达式分配的对象,或者为 0。即使 p 为 0,使用计数为 1 的后置条件也成立;对值为 0 的指针调用 delete 是无害的。 |
|
注意
|
此构造函数是一个模板,以便记住传递的实际指针类型。析构函数将使用相同的指针调用 delete,并保留其原始类型,即使 T 没有虚析构函数,或者为 void。 |
带有删除器的构造函数
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必须是Allocator,如 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可无歧义地转换为enable_shared_from_this<V>*(对于某个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.
|
注意
|
由于临时对象构造和销毁引起的使用计数更新不被视为可观察的副作用,因此实现可以自由地通过不同方式满足效果(和隐含的保证),而无需创建临时对象。 |
|
注意
|
特别地,在示例中
两个赋值可能都是 no-ops。 |
shared_ptr & operator=(shared_ptr && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- 效果
-
等同于
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;
-
- 返回
-
所存储的指针。
unique
bool unique() const noexcept;
-
- 返回
-
.use_count() == 1
use_count
long use_count() const noexcept;
-
- 返回
-
与
*this共享所有权的shared_ptr对象(包括*this)的数量,或者当*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 编译器上,返回值是未指定类型的。 |
交换
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 对象用作关联容器的键。 |
|
注意
|
其他比较运算符因设计原因而被省略。 |
交换
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<T*>( (U*)0 )必须是格式良好的。 - 返回
-
shared_ptr<T>( r, static_cast<typename shared_ptr<T>::element_type*>(r.get()) ).
|
注意
|
看似等价的表达式 shared_ptr<T>(static_cast<T*>(r.get())) 最终将导致未定义行为,尝试删除同一个对象两次。 |
const_pointer_cast
template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
const_cast<T*>( (U*)0 )必须是格式良好的。 - 返回
-
shared_ptr<T>( r, const_cast<typename shared_ptr<T>::element_type*>(r.get()) ).
dynamic_pointer_cast
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
dynamic_cast<T*>( (U*)0 )必须是格式良好的。 - 返回
-
-
当
dynamic_cast<typename shared_ptr<T>::element_type*>(r.get())返回一个非零值p时,shared_ptr<T>(r, p); -
否则,
shared_ptr<T>()。
-
reinterpret_pointer_cast
template<class T, class U>
shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
reinterpret_cast<T*>( (U*)0 )必须是格式良好的。 - 返回
-
shared_ptr<T>( r, reinterpret_cast<typename shared_ptr<T>::element_type*>(r.get()) ).
operator<<
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
-
- 效果
-
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。
原子访问
|
注意
|
本节中的函数相对于第一个 shared_ptr 参数(由 *p 标识)是原子的。对同一 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 以获取完整的示例程序。该程序构建了 shared_ptr 对象的 std::vector 和 std::set。
请注意,在填充容器后,一些 shared_ptr 对象将具有 1 的使用计数而不是 2,因为该集合是 std::set 而不是 std::multiset,因此不包含重复条目。此外,在使用 push_back 和 insert 容器操作期间,使用计数在各种时候可能更高。更复杂的是,容器操作可能在各种情况下抛出异常。在没有智能指针的情况下,要正确处理此示例中的内存管理和异常处理将是一场噩梦。
句柄/体(Handle/Body)惯用法
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 对象提供与内置类型相同的线程安全级别。shared_ptr 实例可以同时被多个线程“读取”(仅使用 const 操作进行访问)。不同的 shared_ptr 实例可以同时被多个线程“写入”(使用可变操作,如 operator= 或 reset 进行访问)(即使这些实例是副本,并且底层共享相同的引用计数)。
任何其他同时访问都会导致未定义行为。
示例
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 release 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 没有提供模板参数来提供 traits 或 policy 以实现广泛的用户自定义?
参数化会劝退用户。
shared_ptr模板经过精心设计,以满足常见需求,而无需 extensive parameterization。 -
我不信服。默认参数可以在适当的时候用来隐藏复杂性。再次,为什么不使用 policy?
模板参数会影响类型。请参阅上面第一个问题的答案。
-
为什么
shared_ptr不使用链表实现?链表实现不能提供足够大的优势来抵消额外指针的成本。此外,使链表实现线程安全成本很高。
-
为什么
shared_ptr(或任何其他 Boost 智能指针)不提供到 T* 的自动转换?自动转换被认为太容易出错。
-
为什么
shared_ptr提供use_count()?作为编写测试用例和调试显示的辅助。其中一个早期版本有
use_count(),它在追踪一个实际上具有循环依赖的复杂项目中的 bug 时很有用。 -
为什么
shared_ptr不指定复杂性要求?因为复杂性要求会限制实现者并使规范复杂化,而对
shared_ptr用户没有明显的好处。例如,如果错误检查实现必须满足严格的复杂性要求,它们可能会变得不符合规范。 -
为什么
shared_ptr不提供release()函数?除非
unique(),否则shared_ptr不能放弃所有权,因为其他副本仍将销毁对象。考虑
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 性。它们这样做意义不大,因为您总是可以从 const 指针获得非 const 指针,然后通过它修改对象。
shared_ptr“尽可能接近原始指针,但仅此而已”。
weak_ptr: 非拥有观察者
描述
weak_ptr 类模板存储对已由 shared_ptr 管理的对象的“弱引用”。要访问该对象,weak_ptr 可以通过接受 weak_ptr 的 shared_ptr 构造函数,或 weak_ptr 成员函数 lock 转换为 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值(r是所持有的值)的weak_ptr。 - 后置条件
-
r为空。
aliasing constructors (别名构造函数)
template<class Y> weak_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> && r, element_type * p) noexcept;
- 效果
-
从
r构造一个weak_ptr,如同使用相应的转换/复制/移动构造函数一样,但存储p而不是。 - 后置条件
-
use_count() == r.use_count()。当!expired()时,shared_ptr<T>(*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<T>(): shared_ptr<T>(*this).
reset
void reset() noexcept;
-
- 效果
-
等价于
weak_ptr().swap(*this)。
交换
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 对象用作关联容器的键。 |
交换
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
-
- 效果
-
等同于
a.swap(b)。
常见问题
-
对象能否在其构造函数中创建指向自身的 weak_ptr?
否。
weak_ptr只能从shared_ptr创建,而在对象构造时,对象尚不存在任何shared_ptr。即使可以创建指向this的临时shared_ptr,它也将在构造函数结束时超出范围,所有weak_ptr实例将立即过期。解决方案是将构造函数设为私有,并提供一个工厂函数,该函数返回一个
shared_ptrclass 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。除了方便和风格之外, such a function is also exception safe and considerably faster because it can use a single allocation for both the object and its corresponding control block, eliminating a significant portion of shared_ptr construction overhead. This eliminates one of the major efficiency complaints about shared_ptr.
make_shared 和 allocate_shared 的重载函数模板系列是为了满足此需求而提供的。make_shared 使用全局 operator new 来分配内存,而 allocate_shared 使用用户提供的分配器,从而允许更精细地控制。
选择名称 make_shared 的理由是,表达式 make_shared<Widget>() 可以大声朗读并传达预期的含义。
最初,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 {template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args> shared_ptr<T> allocate_shared(const A& a, Args&&... args);// T is not an array// T is an array of unknown bounds template<class T> shared_ptr<T> make_shared(std::size_t n); template<class T, class A> shared_ptr<T> allocate_shared(const A& a, std::size_t n);// T is an array of known bounds template<class T> shared_ptr<T> make_shared(); template<class T, class A> shared_ptr<T> allocate_shared(const A& a);// T is an array of unknown bounds template<class T> shared_ptr<T> make_shared(std::size_t n, const remove_extent_t<T>& v); template<class T, class A> shared_ptr<T> allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);// T is an array of known bounds template<class T> shared_ptr<T> make_shared(const remove_extent_t<T>& v); template<class T, class A> shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);// T is not an array of unknown bounds template<class T> shared_ptr<T> make_shared_noinit(); template<class T, class A> shared_ptr<T> allocate_shared_noinit(const A& a);// T is an array of unknown bounds template<class T> shared_ptr<T> make_shared_noinit(std::size_t n); template<class T, class A> shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n); }
通用要求
除非另有说明,否则适用于所有 make_shared 和 allocate_shared 重载的通用要求如下所述。
- 要求
-
A应为一个分配器。A的复制构造函数和析构函数不得抛出异常。 - 效果
-
为类型
T的对象或U的n个对象(如果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>(args)...))执行此初始化,其中p的类型为void*并指向适合容纳类型U的对象的存储。 -
当指定一个非数组类型
U的(子)对象初始化为值v,或从args...构造时,allocate_shared应通过表达式std::allocator_traits<A2>::construct(a2, p, expr)(其中expr分别为v或std::forward<Args>(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<A2>::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不是数组。 - 返回
-
一个
shared_ptr,指向类型为T的对象,该对象由args...构造。 - 示例
-
auto p = make_shared<int>(); -
auto p = make_shared<std::vector<int> >(16, 1);
template<class T>
shared_ptr<T> make_shared(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, std::size_t n);
-
- 限制
-
T是一个未知边界的数组。 - 返回
-
一个
shared_ptr,指向remove_extent_t<T>类型n个值初始化的对象的序列。 - 示例
-
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是一个已知边界的数组。 - 返回
-
一个
shared_ptr,指向remove_extent_t<T>类型extent_v<T>个值初始化的对象的序列。 - 示例
-
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是一个未知边界的数组。 - 返回
-
一个
shared_ptr,指向类型为remove_extent_t<T>的n个对象的序列,每个对象都初始化为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是一个已知边界的数组。 - 返回
-
一个
shared_ptr,指向类型为remove_extent_t<T>的extent_v<T>个对象的序列,每个对象都初始化为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不是数组,或者是一个已知边界的数组。 - 返回
-
一个
shared_ptr,分别指向类型为T的默认初始化对象,或类型为remove_extent_t<T>的extent_v<T>个默认初始化对象的序列。 - 示例
-
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是一个未知边界的数组。 - 返回
-
一个
shared_ptr,指向类型为remove_extent_t<T>的n个默认初始化对象的序列。 - 示例
-
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 的成员函数,它们返回指向 this 的 shared_ptr<T> 和 shared_ptr<T const>(取决于 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() 但仍与其他指向该对象的 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() 但仍与其他指向该对象的 weak_ptr 实例(如果有)共享所有权的 weak_ptr。 |
make_unique: 创建 unique_ptr
描述
make_unique 函数模板提供了方便且安全地创建 std::unique_ptr 对象的方法。
基本原理
C++11 标准引入了 std::unique_ptr,但没有提供像 std::make_shared 那样提供相同异常安全性并且能够避免编写 new 表达式的 make_unique 工具。在被某些标准库供应商实现(并在 C++14 标准引入 std::make_unique 之前),此库由于用户请求而提供了它。
此库还为默认初始化提供了其他 make_unique 重载,当用户不需要或不希望承担值初始化的开销时。C++20 标准现在通过 std::make_unique_for_overwrite 提供了此功能。
提要
make_unique 定义在 <boost/smart_ptr/make_unique.hpp> 中。
namespace boost {template<class T, class... Args> std::unique_ptr<T> make_unique(Args&&... args);// T is not an array// T is not an array template<class T> std::unique_ptr<T> make_unique(type_identity_t<T>&& v);// T is an array of unknown bounds template<class T> std::unique_ptr<T> make_unique(std::size_t n);// T is not an array template<class T> std::unique_ptr<T> make_unique_noinit();// T is an array of unknown bounds template<class T> std::unique_ptr<T> make_unique_noinit(std::size_t n); }
自由函数
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 标准引入了 std::make_unique,它使用 operator new 来创建新对象。但是,标准库中没有方便的工具来为 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>>;template<class T, class A, class... Args> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, Args&&... args);// T is not an array// T is not an array template<class T, class A> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, type_identity_t<T>&& v);// T is an array of unknown bounds template<class T, class A> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, std::size_t n);// T is an array of known bounds template<class T, class A> std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>> allocate_unique(const A& a);// T is an array of unknown bounds template<class T, class A> std::unique_ptr<T, alloc_deleter<T, A>> allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);// T is an array of known bounds template<class T, class A> std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>> allocate_unique(const A& a, const remove_extent_t<T>& v);// T is not an array template<class T, class A> std::unique_ptr<T, alloc_noinit_deleter<T, A>> allocate_unique_noinit(const A& a);// T is an array of unknown bounds template<class T, class A> std::unique_ptr<T, alloc_noinit_deleter<T, A>> allocate_unique_noinit(const A& a, std::size_t n);// T is an array of known bounds template<class T, class A> std::unique_ptr<remove_extent_t<T>[], alloc_noinit_deleter<T, A>> allocate_unique_noinit(const A& a); template<class T, class U, class A> allocator_pointer_t<allocator_rebind_t<A, remove_cv_t<remove_extent_t<T>>>> get_allocator_pointer(const std::unique_ptr<T, alloc_deleter<U, A>>& p) noexcept; }
通用要求
除非另有说明,否则适用于所有 allocate_unique 和 allocate_unique_noinit 重载的通用要求如下所述。
- 要求
-
A应为一个分配器。A的复制构造函数和析构函数不得抛出异常。 - 效果
-
为类型
T的对象或U的n个对象(如果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不是数组。 - 返回
-
一个
std::unique_ptr,指向由args...构造的类型为T的对象。 - 示例
-
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不是数组。 - 返回
-
一个
std::unique_ptr,指向由v构造的类型为T的对象。 - 示例
-
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是一个未知边界的数组。 - 返回
-
一个
std::unique_ptr,指向类型为remove_extent_t<T>的n个值初始化的对象的序列。 - 示例
-
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是一个已知边界的数组。 - 返回
-
一个
std::unique_ptr,指向类型为remove_extent_t<T>的extent_v<T>个值初始化的对象的序列。 - 示例
-
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是一个未知边界的数组。 - 返回
-
一个
std::unique_ptr,指向类型为remove_extent_t<T>的n个对象的序列,每个对象都初始化为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是一个已知边界的数组。 - 返回
-
一个
std::unique_ptr,指向类型为remove_extent_t<T>的extent_v<T>个对象的序列,每个对象都初始化为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不是数组。 - 返回
-
一个
std::unique_ptr,指向类型为T的默认初始化对象。 - 示例
-
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是一个未知边界的数组。 - 返回
-
一个
std::unique_ptr,指向类型为remove_extent_t<T>的n个默认初始化对象的序列。 - 示例
-
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是一个已知边界的数组。 - 返回
-
一个
std::unique_ptr,指向类型为remove_extent_t<T>的extent_v<T>个默认初始化对象的序列。 - 示例
-
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);
Deleter (删除器)
类模板 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 编译器上,返回值是未指定类型的。 |
交换
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 对象用作关联容器的键。 |
交换
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的副本。
交换
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 对象用作关联容器的键。 |
|
注意
|
其他比较运算符因设计原因而被省略。 |
交换
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<T*>( (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<T*>( (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<T*>( (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<T*>( (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 {template<class T, class... Args> local_shared_ptr<T> make_local_shared(Args&&... args); template<class T, class A, class... Args> local_shared_ptr<T> allocate_local_shared(const A& a, Args&&... args);// T is not an array// T is an array of unknown bounds template<class T> local_shared_ptr<T> make_local_shared(std::size_t n); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n);// T is an array of known bounds template<class T> local_shared_ptr<T> make_local_shared(); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a);// T is an array of unknown bounds template<class T> local_shared_ptr<T> make_local_shared(std::size_t n, const remove_extent_t<T>& v); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);// T is an array of known bounds template<class T> local_shared_ptr<T> make_local_shared(const remove_extent_t<T>& v); template<class T, class A> local_shared_ptr<T> allocate_local_shared(const A& a, const remove_extent_t<T>& v);// T is not an array of known bounds template<class T> local_shared_ptr<T> make_local_shared_noinit(); template<class T, class A> local_shared_ptr<T> allocate_local_shared_noinit(const A& a);// T is an array of unknown bounds template<class T> local_shared_ptr<T> make_local_shared_noinit(std::size_t n); template<class T, class A> local_shared_ptr<T> allocate_local_shared_noinit(const A& a, std::size_t n); }
描述
这些函数的. requirements. 和 effects. 与 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);
指针转换函数模板是 static_pointer_cast、dynamic_pointer_cast、const_pointer_cast 和 reinterpret_pointer_cast 对原始指针、std::shared_ptr 和 std::unique_ptr 的重载。这样,在开发与指针类型无关的类时(例如,内存管理器或共享内存兼容类),可以使用相同的代码来处理原始指针和智能指针。
提要
泛型指针转换定义在 <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 参数 intended to be of type 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()。它对于创建使用基于所有权的相等性(而不是默认的指针值相等性)的无序容器很有用。(它也可以与 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++ Idiom 来分离接口和实现是使用抽象基类和工厂函数。抽象类有时被称为“接口”,该模式被称为“面向接口的编程”。同样,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() 减少计数并在计数降至零时销毁自身。
有可能将 COM 对象的指针保存在 shared_ptr 中。
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 永远不会超出对象的生命周期,则使用 空删除器。
在构造函数中获取指向自身的 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;
}
};
获取指向自身的 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;
}
};
解决方案是将指向 this 的弱指针作为成员保存在 impl 中。
class impl: public X, public Y
{
private:
weak_ptr<impl> weak_this;
impl(impl const &);
impl & operator=(impl const &);
impl() { ... }
public:
static shared_ptr<impl> create()
{
shared_ptr<impl> pi(new impl);
pi->weak_this = pi;
return pi;
}
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
shared_ptr<X> px(weak_this);
return px;
}
};
该库现在包含一个辅助类模板 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<void> 持有任意对象
shared_ptr<void> 可以充当通用的对象指针,类似于 void*。当一个构造为
shared_ptr<void> pv(new X);
的 shared_ptr<void> 实例被销毁时,它将通过执行 ~X 正确地处置 X 对象。
这种属性可以以与原始 void* 相同的方式使用,以临时剥离对象指针的类型信息。稍后可以使用 static_pointer_cast 将 shared_ptr<void> 转换回正确的类型。
为异构 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 用作 CopyConstructible 互斥锁
有时需要从函数返回互斥锁,而不可复制的锁不能按值返回。可以使用 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)中描述的 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 向 C++ 标准委员会提交了 auto_ptr 和 counted_ptr 类,它们与我们现在称之为 scoped_ptr 和 shared_ptr 的非常相似。在库工作组的建议很少被全体委员会采纳的情况下,counted_ptr 被拒绝,并将令人惊讶的拥有权转移语义添加到 auto_ptr 中。
1998 年 10 月
Beman Dawes 提议以 safe_ptr 和 counted_ptr 的名称复兴原始语义,会议人员有 Per Andersson、Matt Austern、Greg Colvin、Sean Corfield、Pete Becker、Nico Josuttis、Dietmar Kühl、Nathan Myers、Chichiang Wan 和 Judy Ward。在讨论期间,最终确定了四个新类名,决定不需要完全遵循 std::auto_ptr 接口,并最终确定了各种函数签名和语义。
在接下来的三个月里,对 shared_ptr 进行了几次实现考虑,并在 boost.org 邮件列表中进行了讨论。实现问题围绕必须保留的引用计数,该计数可以附加到被指向的对象,或者分离到别处。这些变体本身又分为两个主要变体。
-
直接分离:
shared_ptr包含指向对象的指针和指向计数的指针。 -
间接分离:
shared_ptr包含指向辅助对象的指针,该辅助对象又包含指向对象和计数的指针。 -
嵌入式附加:计数是所指向对象的成员。
-
放置附加:计数通过 operator new 操作附加。
每种实现技术都有优点和缺点。我们甚至进行了直接和间接方法的各种计时测试,发现在 Intel Pentium 芯片上,测量到的差异非常小。Kevlin Henney 提供了一篇他写的关于“Counted Body Techniques”的论文。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 提供了 shared_ptr 类型的 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 提议 将 shared_ptr 和 weak_ptr 包含在标准库中,通过第一个库技术报告(称为 TR1)。该提案被接受,并最终成为 C++11 标准的一部分。
2007 年 7 月
Peter Dimov 和 Beman Dawes 提议 将 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 提议 将 shared_ptr 扩展到支持数组,并将其包含在标准中,该提案被接受。
2014 年 2 月
Glen Fernandes 更新了 make_shared 和 allocate_shared 以符合 C++ 标准论文 N3870 中的规范,并实现了数组和对象的 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 没有要求;它可以是incomplete type,也可以是 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是一个 complete type。 - 抛出
-
std::bad_alloc。如果抛出异常,将调用delete[] p。
template<class D> shared_array(T* p, D d);
-
- 效果
-
构造一个
shared_array,存储p和d的副本。之后,使用计数为 1。当需要删除p指向的数组时,对象d将在语句d(p)中被使用。 - 要求
-
T是一个 complete type。 -
D的拷贝构造函数和析构函数不得抛出异常。 -
调用参数为
p的对象d时不得抛出异常。- 抛出
-
std::bad_alloc。如果抛出异常,将调用d(p)。
shared_array(const shared_array& v) noexcept;
-
- 效果
-
构造一个
shared_array,如同存储v中存储的指针的副本一样。之后,所有副本的使用计数将比初始使用计数增加 1。 - 要求
-
T是一个 complete type。
析构函数
~shared_array() noexcept;
-
- 效果
-
减少使用计数。然后,如果使用计数为 0,则删除存储指针指向的数组。注意,对值为 0 的指针调用
delete[]是无害的。
赋值
shared_array& operator=(const shared_array& v) noexcept;
-
- 效果
-
构造一个如上所述的新的
shared_array,然后用新的shared_array替换当前的shared_array,并销毁被替换的对象。 - 要求
-
T是一个 complete type。 - 返回
-
*this.
reset
void reset(T* p = 0);
-
- 效果
-
构造一个如上所述的新的
shared_array,然后用新的shared_array替换当前的shared_array,并销毁被替换的对象。 - 要求
-
T是一个 complete type。 - 抛出
-
std::bad_alloc。如果抛出异常,将调用delete[] p。
template<class D> void reset(T* p, D d);
-
- 效果
-
构造一个如上所述的新的
shared_array,然后用新的shared_array替换当前的shared_array,并销毁被替换的对象。 - 要求
-
T是一个 complete type。 -
D的拷贝构造函数不得抛出异常。- 抛出
-
std::bad_alloc。如果抛出异常,将调用d(p)。
索引
T& operator[](std::ptrdiff_t n) const noexcept;
- 返回
-
对存储指针指向的数组的第
n个元素的引用。如果存储指针为 0,或者n小于 0 或大于等于数组中的元素数量,则行为未定义且几乎肯定是不期望的。 - 要求
-
T是一个 complete type。
get
T* get() const noexcept;
-
- 返回
-
所存储的指针。
unique
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是一个 complete type。
交换
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)。 |
交换
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
-
- 返回
-
a.swap(b). - 要求
-
T是一个 complete type。
附录 D: 版权与许可
本文档
-
Copyright 1999 Greg Colvin
-
Copyright 1999 Beman Dawes
-
Copyright 2002 Darin Adler
-
Copyright 2003-2020 Peter Dimov
-
Copyright 2005, 2006 Ion Gaztañaga
-
Copyright 2008 Frank Mori Hess
-
Copyright 2012-2017 Glen Fernandes
-
Copyright 2013 Andrey Semashev