简介
智能指针是存储指向动态分配(堆)对象的指针的对象。 它们的行为很像内置 C++ 指针,除了它们会在适当的时候自动删除指向的对象。 智能指针在面对异常时特别有用,因为它们确保动态分配对象的正确销毁。 它们也可以用于跟踪由多个所有者共享的动态分配对象。
从概念上讲,智能指针被视为拥有指向的对象,因此负责在不再需要该对象时删除该对象。 因此,它们是 Bjarne Stroustrup 的 “C++ 编程语言” 第 3 版第 14.4 节资源管理中描述的 “资源获取即初始化” 惯用法的示例。
此库提供了六个智能指针类模板
-
scoped_ptr
,用于将动态分配对象的所有权限制在当前作用域内; -
scoped_array
,为动态分配数组提供作用域所有权; -
shared_ptr
,用于管理对象或数组的共享所有权的通用工具; -
weak_ptr
,shared_ptr
管理的对象的非拥有观察者,可以临时提升为shared_ptr
; -
intrusive_ptr
,指向具有嵌入式引用计数的对象的指针; -
local_shared_ptr
,在单个线程内提供共享所有权。
shared_ptr
和 weak_ptr
自 2011 年迭代以来一直是 C++ 标准的一部分。
此外,该库还包含以下支持实用程序函数和类
-
make_shared
和allocate_shared
,用于创建返回shared_ptr
的对象的工厂函数; -
make_unique
,返回std::unique_ptr
的工厂函数; -
allocate_unique
,用于创建使用分配器并返回std::unique_ptr
的对象的工厂函数; -
enable_shared_from_this
,一个辅助基类,使获取指向this
的shared_ptr
成为可能; -
enable_shared_from
,更新且更好的enable_shared_from_this
替代品; -
pointer_to_other
,用于转换一种智能指针类型为另一种类型的辅助 trait; -
static_pointer_cast
及其同伴,通用智能指针转换; -
intrusive_ref_counter
,包含引用计数的辅助基类。 -
atomic_shared_ptr
,一个辅助类,为shared_ptr
类型的值实现std::atomic
的接口。
作为一般规则,库中指针管理的对象的析构函数或 operator delete
不允许抛出异常。
修订历史
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
是指向的对象的类型。 销毁 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 编译器上,返回值是未指定的类型。 |
swap
void swap(scoped_ptr & b) noexcept;
交换两个智能指针的内容。 T
不需要是完整类型。
自由函数
swap
template<class T> void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) noexcept;
等效于 a.swap(b)
。
比较
template<class T> bool operator==( scoped_ptr<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator==( std::nullptr_t, scoped_ptr<T> const & p ) noexcept;
返回 p.get() == nullptr
。
template<class T> bool operator!=( scoped_ptr<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator!=( std::nullptr_t, scoped_ptr<T> const & p ) noexcept;
返回 p.get() != nullptr
。
示例
这是一个使用 scoped_ptr
的示例。
#include <boost/scoped_ptr.hpp>
#include <iostream>
struct Shoe { ~Shoe() { std::cout << "Buckle my shoe\n"; } };
class MyClass {
boost::scoped_ptr<int> ptr;
public:
MyClass() : ptr(new int) { *ptr = 0; }
int add_one() { return ++*ptr; }
};
int main()
{
boost::scoped_ptr<Shoe> x(new Shoe);
MyClass my_instance;
std::cout << my_instance.add_one() << '\n';
std::cout << my_instance.add_one() << '\n';
}
示例程序生成了一首儿童童谣的开头
1
2
Buckle my shoe
理由
使用 scoped_ptr
而不是 std::auto_ptr
或 std::unique_ptr
的主要原因是让代码的读者知道您打算仅对当前作用域应用 “资源获取即初始化”,并且无意转移所有权。
使用 scoped_ptr
的第二个原因是防止以后的维护程序员添加通过返回 auto_ptr
来转移所有权的函数,因为维护程序员看到了 auto_ptr
,并假设可以安全地转移所有权。
想想 bool
与 int
。 我们都知道,在底层,bool
通常只是一个 int
。 实际上,有些人反对在 C++ 标准中包含 bool,因为那样做。 但是,通过编写 bool
而不是 int
,您可以告诉读者您的意图是什么。 scoped_ptr
也是如此; 通过使用它,您是在发出意图信号。
有人建议 scoped_ptr<T>
等效于 std::auto_ptr<T> const
。 然而,Ed Brey 指出,reset
在 std::auto_ptr<T> const
上不起作用。
句柄/主体 惯用法
scoped_ptr
的一个常见用法是实现句柄/主体(也称为 pimpl)惯用法,以避免在头文件中暴露主体(实现)。
scoped_ptr_example_test.cpp
示例程序包含一个头文件 scoped_ptr_example.hpp
,该文件使用指向不完整类型的 scoped_ptr<>
来隐藏实现。 需要完整类型的成员函数的实例化发生在 scoped_ptr_example.cpp
实现文件中。
常见问题解答
-
为什么
scoped_ptr
没有release()
成员?在阅读源代码时,能够根据使用的类型对程序行为得出结论非常有价值。 如果
scoped_ptr
具有release()
成员,则可以转移所持指针的所有权,从而削弱其作为将资源生命周期限制在给定上下文中的作用。 在需要转移所有权的情况下,请使用std::auto_ptr
。(由 Dave Abrahams 提供)
scoped_array:作用域数组所有权
描述
scoped_array
类模板存储指向动态分配数组的指针。(动态分配数组使用 C++ new[]
表达式分配。)指向的数组保证会被删除,无论是在 scoped_array
销毁时,还是通过显式的 reset
删除。
scoped_array
模板是满足简单需求的简单解决方案。 它提供了一个基本的 “资源获取即初始化” 工具,没有共享所有权或所有权转移语义。 它的名称和语义的强制执行(通过不可复制)都表明了其意图,即仅在当前作用域内保留所有权。 因为它是不可复制的,所以对于不应复制的指针 shared_ptr<T[]>
更安全。
由于 scoped_array
非常简单,因此在其常用实现中,每个操作都与内置数组指针一样快,并且没有比内置数组指针更多的空间开销。
它不能在 C++ 标准库容器中使用。 如果 scoped_array
不能满足您的需求,请参阅 shared_ptr<T[]>
。
它不能正确地保存指向单个对象的指针。 有关该用法,请参阅 scoped_ptr
。
std::vector
是 scoped_array
的替代方案,它更重型但更灵活。 boost::array
是不使用动态分配的替代方案。
类模板在 T
上参数化,T
是指向的对象的类型。
概要
scoped_array
在 <boost/smart_ptr/scoped_array.hpp>
中定义。
namespace boost {
template<class T> class scoped_array {
private:
scoped_array(scoped_array const &);
scoped_array & operator=(scoped_array const &);
void operator==( scoped_array const& ) const;
void operator!=( scoped_array const& ) const;
public:
typedef T element_type;
explicit scoped_array(T * p = 0) noexcept;
~scoped_array() noexcept;
void reset(T * p = 0) noexcept;
T & operator[](std::ptrdiff_t i) const noexcept;
T * get() const noexcept;
explicit operator bool () const noexcept;
void swap(scoped_array & b) noexcept;
};
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
template<class T>
bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;
template<class T>
bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
}
成员
element_type
typedef T element_type;
提供存储指针的类型。
构造函数
explicit scoped_array(T * p = 0) noexcept;
构造一个 scoped_array
,存储 p
的副本,p
必须已通过 C++ new[]
表达式分配或为 0。 T
不需要是完整类型。
析构函数
~scoped_array() noexcept;
删除存储指针指向的数组。 请注意,对值为 0 的指针执行 delete[]
是无害的。 T
必须是完整的,并且对存储指针执行 delete[]
不得抛出异常。
reset
void reset(T * p = 0) noexcept;
删除存储指针指向的数组,然后存储 p
的副本,p
必须已通过 C++ new[]
表达式分配或为 0。 T
必须是完整的,并且对存储指针执行 delete[]
不得抛出异常。
下标
T & operator[](std::ptrdiff_t i) const noexcept;
返回对存储指针指向的数组的元素 i
的引用。 如果存储指针为 0,或者如果 i
小于 0 或大于或等于数组中的元素数,则行为未定义,并且几乎可以肯定是不希望出现的。
get
T * get() const noexcept;
返回存储的指针。 T
不需要是完整类型。
转换
explicit operator bool () const noexcept;
返回 get() != 0
。
注意
|
在 C++03 编译器上,返回值是未指定的类型。 |
swap
void swap(scoped_array & b) noexcept;
交换两个智能指针的内容。 T
不需要是完整类型。
自由函数
swap
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
等效于 a.swap(b)
。
比较
template<class T> bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;
返回 p.get() == nullptr
。
template<class T> bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
返回 p.get() != nullptr
。
shared_ptr:共享所有权
描述
shared_ptr
类模板存储指向动态分配对象的指针,通常使用 C++ new
表达式。 当指向它的最后一个 shared_ptr
被销毁或重置时,指向的对象保证会被删除。
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );
shared_ptr
删除在构造时传递的确切指针,并带有其原始类型,而与模板参数无关。 在上面的第二个示例中,当 p2
被销毁或重置时,它将对已传递给构造函数的原始 int*
调用 delete
,即使 p2
本身是 shared_ptr<void>
类型并存储 void*
类型的指针。
每个 shared_ptr
都满足 C++ 标准库的 CopyConstructible
、MoveConstructible
、CopyAssignable
和 MoveAssignable
要求,并且可以在标准库容器中使用。 提供了比较运算符,以便 shared_ptr
可以与标准库的关联容器一起使用。
由于实现使用引用计数,因此 shared_ptr
实例的循环将不会被回收。 例如,如果 main()
持有指向 A
的 shared_ptr
,而 A
直接或间接地持有返回到 A
的 shared_ptr
,则 A
的使用计数将为 2。销毁原始的 shared_ptr
将使 A
悬空,使用计数为 1。 使用 weak_ptr
来 “打破循环”。
类模板在 T
上参数化,T
是指向的对象的类型。 shared_ptr
及其大多数成员函数对 T
没有要求; 它允许是不完整类型或 void
。 确实有附加要求的成员函数(构造函数、reset
)在下面明确记录。
每当 T*
可以隐式转换为 U*
时,shared_ptr<T>
都可以隐式转换为 shared_ptr<U>
。 特别是,shared_ptr<T>
可以隐式转换为 shared_ptr<T const>
,转换为 shared_ptr<U>
,其中 U
是 T
的可访问基类,以及转换为 shared_ptr<void>
。
shared_ptr
现在是 C++11 标准的一部分,即 std::shared_ptr
。
从 Boost 1.53 版本开始,shared_ptr
可以用于保存指向动态分配数组的指针。 这是通过使用数组类型(T[]
或 T[N]
)作为模板参数来实现的。 使用无大小数组 T[]
和有大小数组 T[N]
之间几乎没有区别; 后者仅使 operator[]
能够对索引执行范围检查。
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );
最佳实践
几乎消除内存泄漏可能性的简单指南是:始终使用命名的智能指针变量来保存 new
的结果。 代码中 new
关键字的每次出现都应采用以下形式
shared_ptr<T> p(new Y);
当然,可以使用另一个智能指针代替上面的 shared_ptr
; 让 T
和 Y
成为相同类型,或者将参数传递给 Y
的构造函数也是可以的。
如果遵守此指南,自然而然地,您将没有显式的 delete
语句; try
/catch
构造将很少见。
避免使用未命名的 shared_ptr
临时对象来节省打字; 要了解为什么这很危险,请考虑以下示例
void f(shared_ptr<int>, int);
int g();
void ok()
{
shared_ptr<int> p( new int(2) );
f( p, g() );
}
void bad()
{
f( shared_ptr<int>( new int(2) ), g() );
}
函数 ok
完全遵循该指南,而 bad
就地构造临时 shared_ptr
,从而导致内存泄漏的可能性。 由于函数参数以未指定的顺序进行评估,因此 new int(2)
可能首先被评估,g()
第二个被评估,并且如果 g
抛出异常,我们可能永远无法到达 shared_ptr
构造函数。 有关更多信息,请参阅 Herb Sutter 的处理方法。
通过使用 <boost/smart_ptr/make_shared.hpp>
中定义的 make_shared
或 allocate_shared
工厂函数,也可以消除上面描述的异常安全性问题。 这些工厂函数还通过合并分配来提供效率优势。
概要
shared_ptr
在 <boost/smart_ptr/shared_ptr.hpp>
中定义。
namespace boost {
class bad_weak_ptr: public std::exception;
template<class T> class weak_ptr;
template<class T> class shared_ptr {
public:
typedef /*see below*/ element_type;
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
~shared_ptr() noexcept;
shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r, element_type * p) noexcept;
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(shared_ptr const & r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;
shared_ptr & operator=(shared_ptr const && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(std::nullptr_t) noexcept;
void reset() noexcept;
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
T & operator*() const noexcept; // only valid when T is not an array type
T * operator->() const noexcept; // only valid when T is not an array type
// only valid when T is an array type
element_type & operator[](std::ptrdiff_t i) const noexcept;
element_type * get() const noexcept;
bool unique() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
void swap(shared_ptr & b) noexcept;
template<class Y> bool owner_before(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_before(weak_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(weak_ptr<Y> const & r) const noexcept;
std::size_t owner_hash_value() const noexcept;
};
template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
template<class T>
typename shared_ptr<T>::element_type *
get_pointer(shared_ptr<T> const & p) noexcept;
template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
template<class D, class T> D * get_deleter(shared_ptr<T> const & p) noexcept;
template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;
template<class T> shared_ptr<T> atomic_load( shared_ptr<T> const * p ) noexcept;
template<class T>
shared_ptr<T> atomic_load_explicit( shared_ptr<T> const * p, int ) noexcept;
template<class T>
void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
void atomic_store_explicit( shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
template<class T>
bool atomic_compare_exchange(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w ) noexcept;
template<class T>
bool atomic_compare_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w, int, int ) noexcept;
}
成员
element_type
typedef ... element_type;
当 T
不是数组类型时,element_type
为 T
,当 T
为 U[]
或 U[N]
时,element_type
为 U
。
默认构造函数
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
-
- 效果
-
构造一个空的
shared_ptr
。 - 后置条件
-
use_count() == 0 && get() == 0
.
指针构造函数
template<class Y> explicit shared_ptr(Y * p);
-
- 要求
-
Y
必须是完整类型。 当T
是数组类型时,表达式delete[] p
,或者当T
不是数组类型时,表达式delete p
必须是良构的、良好定义的,并且不得抛出异常。 当T
为U[N]
时,Y(*)[N]
必须可转换为T*
; 当T
为U[]
时,Y(*)[]
必须可转换为T*
; 否则,Y*
必须可转换为T*
。 - 效果
-
当
T
不是数组类型时,构造一个拥有指针p
的shared_ptr
。 否则,构造一个拥有p
和未指定类型的析构器(调用delete[] p
)的shared_ptr
。 - 后置条件
-
use_count() == 1 && get() == p
。 如果T
不是数组类型,并且p
可以明确转换为某个V
的enable_shared_from_this<V>*
,则p->shared_from_this()
返回*this
的副本。 - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。 - 异常安全性
-
如果抛出异常,则当
T
是数组类型时,构造函数调用delete[] p
,或者当T
不是数组类型时,构造函数调用delete p
。
注意
|
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 的构造函数使用a
的副本分配内存。 - 后置条件
-
use_count() == 1 && get() == p
。 如果T
不是数组类型,并且p
可以明确转换为某个V
的enable_shared_from_this<V>*
,则p->shared_from_this()
返回*this
的副本。 - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。 - 异常安全性
-
如果抛出异常,则调用
d(p)
。
注意
|
当删除 p 指向的对象的时间到来时,将使用存储的 p 副本作为参数调用 d 的存储副本。 |
注意
|
自定义析构器允许返回 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
共享所有权并存储r
中存储的指针副本的shared_ptr
。 - 后置条件
-
use_count() == r.use_count()
. - 抛出
-
当
r.use_count() == 0
时为bad_weak_ptr
。 - 异常安全性
-
如果抛出异常,则构造函数不起作用。
auto_ptr 构造函数
template<class Y> shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
-
- 要求
-
Y*
应该可转换为T*
。 - 效果
-
构造一个
shared_ptr
,就像通过存储r.release()
的副本一样。 - 后置条件
-
use_count() == 1
. - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。 - 异常安全性
-
如果抛出异常,则构造函数不起作用。
unique_ptr 构造函数
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
-
- 要求
-
Y*
应该可转换为T*
。 - 效果
-
-
当
r.get() == 0
时,等效于shared_ptr()
; -
当
D
不是引用类型时,等效于shared_ptr(r.release(), r.get_deleter())
; -
否则,等效于
shared_ptr(r.release(), del)
,其中del
是一个析构器,它存储从r.get_deleter()
返回的引用rd
,并且del(p)
调用rd(p)
。
-
- 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。 - 异常安全性
-
如果抛出异常,则构造函数不起作用。
析构函数
~shared_ptr() noexcept;
-
- 效果
-
-
如果
*this
为空,或者与另一个shared_ptr
实例共享所有权 (use_count() > 1
),则没有副作用。 -
否则,如果
*this
拥有指针p
和析构器d
,则调用d(p)
。 -
否则,
*this
拥有指针p
,并且调用delete p
。
-
赋值
shared_ptr & operator=(shared_ptr const & r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
-
- 效果
-
等效于
shared_ptr(r).swap(*this)
。 - 返回
-
*this
.
注意
|
由临时对象构造和销毁引起的使用计数更新不被认为是可观察的副作用,并且实现可以自由地通过不同的方式满足效果(和隐含的保证),而无需创建临时对象。 |
注意
|
特别是在示例中
两个赋值都可能是空操作。 |
shared_ptr & operator=(shared_ptr && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- 效果
-
等效于
shared_ptr(std::move(r)).swap(*this)
。 - 返回
-
*this
.
shared_ptr & operator=(std::nullptr_t) noexcept;
-
- 效果
-
等效于
shared_ptr().swap(*this)
。 - 返回
-
*this
.
reset
void reset() noexcept;
-
- 效果
-
等效于
shared_ptr().swap(*this)
。
template<class Y> void reset(Y * p);
-
- 效果
-
等效于
shared_ptr(p).swap(*this)
。
template<class Y, class D> void reset(Y * p, D d);
-
- 效果
-
等效于
shared_ptr(p, d).swap(*this)
。
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- 效果
-
等效于
shared_ptr(p, d, a).swap(*this)
。
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
等效于
shared_ptr(r, p).swap(*this)
。
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
等效于
shared_ptr(std::move(r), p).swap(*this)
。
间接引用
T & operator*() const noexcept;
-
- 要求
-
T
不应为数组类型。 存储的指针不得为 0。 - 返回
-
*get()
.
T * operator->() const noexcept;
-
- 要求
-
T
不应为数组类型。 存储的指针不得为 0。 - 返回
-
get()
.
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- 要求
-
T
应为数组类型。 存储的指针不得为 0。i >= 0
。 如果T
为U[N]
,则i < N
。 - 返回
-
get()[i]
.
get
element_type * get() const noexcept;
-
- 返回
-
存储的指针。
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 编译器上,返回值是未指定的类型。 |
swap
void swap(shared_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
owner_before
template<class Y> bool owner_before(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_before(weak_ptr<Y> const & r) const noexcept;
-
- 返回
-
请参阅
operator<
的描述。
owner_equals
template<class Y> bool owner_equals(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(weak_ptr<Y> const & r) const noexcept;
-
- 返回
-
true
,当且仅当*this
和r
共享所有权或两者都为空时。
owner_hash_value
std::size_t owner_hash_value() const noexcept;
-
- 返回
-
一个未指定的哈希值,使得共享所有权的两个实例具有相同的哈希值。
自由函数
comparison
template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 返回
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 返回
-
a.get() != b.get()
.
template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
-
- 返回
-
p.get() == 0
.
template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
-
- 返回
-
p.get() != 0
.
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 返回
-
一个未指定的值,使得
-
operator<
是 C++ 标准 [lib.alg.sorting] 节中描述的严格弱序关系; -
在由
operator<
定义的等价关系下,!(a < b) && !(b < a)
,两个shared_ptr
实例等价当且仅当它们共享所有权或两者都为空。
-
注意
|
允许将 shared_ptr 对象用作关联容器中的键。 |
注意
|
出于设计考虑,省略了其余的比较运算符。 |
swap
template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
-
- 效果
-
等效于
a.swap(b)
。
get_pointer
template<class T>
typename shared_ptr<T>::element_type *
get_pointer(shared_ptr<T> const & p) noexcept;
-
- 返回
-
p.get()
.
注意
|
作为泛型编程的辅助手段提供。供 mem_fn 使用。 |
static_pointer_cast
template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
static_cast<T*>( (U*)0 )
必须是良构的。 - 返回
-
shared_ptr<T>( r, static_cast<typename shared_ptr<T>::element_type*>(r.get()) )
.
Caution
|
看似等价的表达式 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-非限定)D
的删除器d
,则返回&d
;否则返回 0。
Atomic Access
注意
|
本节中的函数关于第一个 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
。 - Remarks
-
如果两个
shared_ptr
实例存储相同的指针值并且共享所有权,则它们是等价的。
示例
有关完整的示例程序,请参见 shared_ptr_example.cpp。该程序构建了一个 std::vector
和一个 std::set
的 shared_ptr
对象。
请注意,在容器填充后,某些 shared_ptr
对象的使用计数将为 1 而不是 2,因为该集合是 std::set
而不是 std::multiset
,因此不包含重复条目。此外,在执行 push_back
和 insert
容器操作时,使用计数在不同时间可能会更高。更复杂的是,容器操作可能在各种情况下抛出异常。如果没有智能指针,在这个例子中正确处理内存管理和异常处理将是一场噩梦。
句柄/主体 惯用法
shared_ptr
的一个常见用法是实现句柄/主体(也称为 pimpl)惯用法,这避免在头文件中暴露主体(实现)。
shared_ptr_example2_test.cpp 示例程序包含一个头文件 shared_ptr_example2.hpp,该文件使用指向不完整类型的 shared_ptr
来隐藏实现。需要完整类型的成员函数的实例化发生在 shared_ptr_example2.cpp 实现文件中。请注意,不需要显式析构函数。与 ~scoped_ptr
不同,~shared_ptr
不要求 T
是完整类型。
线程安全
shared_ptr
对象提供与内置类型相同级别的线程安全性。 shared_ptr
实例可以被多个线程同时“读取”(仅使用 const 操作访问)。不同的 shared_ptr
实例可以被多个线程同时“写入”(使用可变操作(如 operator=
或 reset
)访问)(即使这些实例是副本,并且在底层共享相同的引用计数。)
任何其他并发访问都会导致未定义的行为。
Examples
shared_ptr<int> p(new int(42));
shared_ptr
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
shared_ptr
实例// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
shared_ptr
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
shared_ptr
// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
shared_ptr
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
从 Boost 1.33.0 版本开始,shared_ptr
在大多数常见平台上使用无锁实现。
如果您的程序是单线程的,并且不链接到任何可能在其默认配置中使用 shared_ptr
的库,您可以在项目范围内 #define
宏 BOOST_SP_DISABLE_THREADS
以切换到普通的非原子引用计数更新。
(在某些但并非所有翻译单元中定义 BOOST_SP_DISABLE_THREADS
在技术上违反了单一定义规则和未定义行为。然而,实现会尽力满足在这些翻译单元中使用非原子更新的请求。但是,不保证。)
您可以定义宏 BOOST_SP_USE_PTHREADS
以关闭无锁的平台特定实现,并回退到通用的基于 pthread_mutex_t
的代码。
常见问题解答
-
共享指针有几种变体,具有不同的权衡;为什么智能指针库只提供一个实现?能够尝试每种类型以找到最适合手头工作的类型是否会很有用?
shared_ptr
的一个重要目标是提供标准的共享所有权指针。拥有单一的指针类型对于稳定的库接口非常重要,因为不同的共享指针通常无法互操作,即,引用计数指针(库 A 使用)无法与链接指针(库 B 使用)共享所有权。 -
为什么 shared_ptr 没有提供 traits 或 policies 的模板参数来允许广泛的用户自定义?
参数化会阻止用户。
shared_ptr
模板经过精心设计,可以在没有广泛参数化的情况下满足常见需求。 -
我并不信服。默认参数可以在适当的地方使用以隐藏复杂性。再说一次,为什么不使用 policies 呢?
模板参数会影响类型。请参阅上面第一个问题的答案。
-
为什么
shared_ptr
不使用链表实现?链表实现没有提供足够的优势来抵消额外指针的增加成本。此外,使链表实现线程安全成本很高。
-
为什么
shared_ptr
(或任何其他 Boost 智能指针)不提供到 T* 的自动转换?自动转换被认为太容易出错。
-
为什么
shared_ptr
提供use_count()
?作为编写测试用例和调试显示的辅助手段。其中一个始祖具有
use_count()
,并且它在跟踪一个复杂项目中被证明有循环依赖关系的错误时非常有用。 -
为什么
shared_ptr
不指定复杂度要求?因为复杂度要求限制了实现者并使规范复杂化,而对
shared_ptr
用户没有明显的好处。例如,如果错误检查实现必须满足严格的复杂度要求,则它们可能会变得不符合规范。 -
为什么
shared_ptr
不提供release()
函数?除非
shared_ptr
是unique()
的,否则它不能放弃所有权,因为另一个副本仍然会销毁该对象。Consider
shared_ptr<int> a(new int); shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2 int * p = a.release(); // Who owns p now? b will still call delete on it in its destructor.
此外,
release()
返回的指针将难以可靠地释放,因为源shared_ptr
可能是使用自定义删除器创建的,或者可能指向不同类型的对象。 -
为什么
operator->()
是 const,但其返回值是指向元素类型的非 const 指针?浅拷贝指针(包括原始指针)通常不传播常量性。这样做意义不大,因为您始终可以从 const 指针获得非 const 指针,然后通过它修改对象。
shared_ptr
“尽可能接近原始指针,但不再接近”。
weak_ptr:非拥有观察者
描述
weak_ptr
类模板存储对已由 shared_ptr
管理的对象的“弱引用”。要访问对象,可以使用接受 weak_ptr
的 shared_ptr
构造函数,或 weak_ptr
成员函数 lock
将 weak_ptr
转换为 shared_ptr
。当指向对象的最后一个 shared_ptr
消失且对象被删除时,尝试从引用已删除对象的 weak_ptr
实例获取 shared_ptr
将会失败:构造函数将抛出 boost::bad_weak_ptr
类型的异常,并且 weak_ptr::lock
将返回一个空的 shared_ptr
。
每个 weak_ptr
都满足 C++ 标准库的 CopyConstructible
和 Assignable
要求,因此可以在标准库容器中使用。提供了比较运算符,以便 weak_ptr
可以与标准库的关联容器一起使用。
weak_ptr
操作永远不会抛出异常。
类模板在 T
上参数化,T
是指向的对象的类型。
与 shared_ptr
相比,weak_ptr
提供的操作子集非常有限,因为访问其存储的指针在多线程程序中通常是危险的,有时即使在单线程中也是不安全的(也就是说,它可能会调用未定义的行为。)假设 weak_ptr
有一个返回原始指针的 get 成员函数,并考虑这段看似无辜的代码
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(int * r = q.get())
{
// use *r
}
假设在 if
之后,但在使用 r
之前,另一个线程执行语句 p.reset()
。现在 r
是一个悬空指针。
解决此问题的方法是从 q
创建一个临时的 shared_ptr
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(shared_ptr<int> r = q.lock())
{
// use *r
}
现在 r
持有对 q
指向的对象的引用。即使在另一个线程中执行 p.reset()
,该对象也会保持活动状态,直到 r
超出范围或被重置。通过获取对象的 shared_ptr
,我们有效地锁定了它以防止销毁。
概要
weak_ptr
在 <boost/smart_ptr/weak_ptr.hpp>
中定义。
namespace boost {
template<class T> class weak_ptr {
public:
typedef /*see below*/ element_type;
weak_ptr() noexcept;
template<class Y> weak_ptr(shared_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr const & r) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr && r) noexcept;
template<class Y> weak_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> && r, element_type * p) noexcept;
~weak_ptr() noexcept;
weak_ptr & operator=(weak_ptr const & r) noexcept;
weak_ptr & operator=(weak_ptr && r) noexcept;
template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r) noexcept;
template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r) noexcept;
long use_count() const noexcept;
bool expired() const noexcept;
bool empty() const noexcept;
shared_ptr<T> lock() const noexcept;
void reset() noexcept;
void swap(weak_ptr<T> & b) noexcept;
template<class Y> bool owner_before( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_before( shared_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( shared_ptr<Y> const & r ) const noexcept;
std::size_t owner_hash_value() const noexcept;
};
template<class T, class U>
bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
}
成员
element_type
typedef ... element_type;
当 T
不是数组类型时,element_type
为 T
,当 T
为 U[]
或 U[N]
时,element_type
为 U
。
构造函数
weak_ptr() noexcept;
-
- 效果
-
构造一个空的
weak_ptr
。 - 后置条件
-
use_count() == 0
.
template<class Y> weak_ptr(shared_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr const & r) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r) noexcept;
-
- 效果
-
如果
r
为空,则构造一个空的weak_ptr
;否则,构造一个与r
共享所有权的weak_ptr
,就像存储r
中存储的指针的副本一样。 - 后置条件
-
use_count() == r.use_count()
.
weak_ptr(weak_ptr && r) noexcept;
-
- 效果
-
构造一个持有值
r
的weak_ptr
。 - 后置条件
-
r
为空。
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)
。
swap
void swap(weak_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
owner_before
template<class Y> bool owner_before( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_before( shared_ptr<Y> const & r ) const noexcept;
-
- 返回
-
请参阅
operator<
的描述。
owner_equals
template<class Y> bool owner_equals( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( shared_ptr<Y> const & r ) const noexcept;
-
- 返回
-
true
,当且仅当*this
和r
共享所有权或两者都为空时。
owner_hash_value
std::size_t owner_hash_value() const noexcept;
-
- 返回
-
一个未指定的哈希值,使得共享所有权的两个实例具有相同的哈希值。
自由函数
comparison
template<class T, class U>
bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
-
- 返回
-
一个未指定的值,使得
-
operator<
是 C++ 标准 [lib.alg.sorting] 节中描述的严格弱序关系; -
在由
operator<
定义的等价关系下,!(a < b) && !(b < a)
,两个weak_ptr
实例等价当且仅当它们共享所有权或两者都为空。
-
注意
|
允许将 weak_ptr 对象用作关联容器中的键。 |
swap
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
-
- 效果
-
等效于
a.swap(b)
。
常见问题解答
-
对象可以在其构造函数中创建指向自身的 weak_ptr 吗?
不能。
weak_ptr
只能从shared_ptr
创建,并且在对象构造时,尚不存在指向该对象的shared_ptr
。即使您可以创建指向this
的临时shared_ptr
,它也会在构造函数末尾超出范围,并且所有weak_ptr
实例都会立即过期。解决方案是将构造函数设为私有,并提供一个返回
shared_ptr
的工厂函数class X { private: X(); public: static shared_ptr<X> create() { shared_ptr<X> px(new X); // create weak pointers from px here return px; } };
make_shared:创建 shared_ptr
描述
函数模板 make_shared
和 allocate_shared
提供了创建 shared_ptr
对象的便捷、安全和高效的方式。
理由
一致地使用 shared_ptr
可以消除使用显式 delete
的需要,但它本身并不能避免显式的 new
。用户反复要求提供一个工厂函数,该函数创建一个给定类型的对象并返回指向它的 shared_ptr
。除了方便和风格之外,这样的函数也是异常安全的,并且速度快得多,因为它可以使用单个分配来分配对象及其相应的控制块,从而消除了 shared_ptr
构造开销的很大一部分。这消除了关于 shared_ptr
的主要效率抱怨之一。
提供的重载函数模板系列 make_shared
和 allocate_shared
就是为了解决此需求。 make_shared
使用全局 operator new
来分配内存,而 allocate_shared
使用用户提供的分配器,从而允许更精细的控制。
选择名称 make_shared
的理由是,表达式 make_shared<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 {
// T is not an array
template<class T, class... Args>
shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
shared_ptr<T> allocate_shared(const A& a, Args&&... args);
// T is an array of unknown bounds
template<class T>
shared_ptr<T> make_shared(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, std::size_t n);
// T is an array of known bounds
template<class T>
shared_ptr<T> make_shared();
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a);
// T is an array of unknown bounds
template<class T> shared_ptr<T>
make_shared(std::size_t n, const remove_extent_t<T>& v);
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
// T is an array of known bounds
template<class T>
shared_ptr<T> make_shared(const remove_extent_t<T>& v);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);
// T is not an array of unknown bounds
template<class T>
shared_ptr<T> make_shared_noinit();
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a);
// T is an array of unknown bounds
template<class T>
shared_ptr<T> make_shared_noinit(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n);
}
通用要求
除非另有说明,否则适用于所有 make_shared
和 allocate_shared
重载的通用要求如下所述。
- 要求
-
A
应该是分配器。A
的复制构造函数和析构函数不应抛出异常。 - 效果
-
为类型
T
的对象或类型U
的n
个对象(如果T
是U[]
形式的数组类型,并且n
由参数确定,如具体重载所指定)分配内存。对象从参数初始化,如具体重载所指定。使用a
的重新绑定的副本(对于未指定的value_type
)来分配内存。如果抛出异常,则这些函数无效。 - 返回
-
一个
shared_ptr
实例,用于存储和拥有新构造对象的地址。 - 后置条件
-
r.get() != 0
且r.use_count() == 1
,其中r
是返回值。 - 抛出
-
std::bad_alloc
,从A::allocate
或从对象的初始化抛出的异常。 - Remarks
-
-
执行不超过一次内存分配。这提供了与侵入式智能指针相当的效率。
-
当指定将数组类型的对象初始化为相同类型
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);
-
- Constraints
-
T
不是数组。 - 返回
-
指向类型为
T
的对象的shared_ptr
,从args...
构造。 - Examples
-
auto p = make_shared<int>();
-
auto p = make_shared<std::vector<int> >(16, 1);
template<class T>
shared_ptr<T> make_shared(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, std::size_t n);
-
- Constraints
-
T
是未知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的n
个值初始化对象的序列的shared_ptr
。 - Examples
-
auto p = make_shared<double[]>(1024);
-
auto p = make_shared<double[][2][2]>(6);
template<class T>
shared_ptr<T> make_shared();
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a);
-
- Constraints
-
T
是已知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的extent_v<T>
个值初始化对象的序列的shared_ptr
。 - Examples
-
auto p = make_shared<double[1024]>();
-
auto p = make_shared<double[6][2][2]>();
template<class T> shared_ptr<T>
make_shared(std::size_t n, const remove_extent_t<T>& v);
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
-
- Constraints
-
T
是未知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的n
个对象的序列的shared_ptr
,每个对象都初始化为v
。 - Examples
-
auto p = make_shared<double[]>(1024, 1.0);
-
auto p = make_shared<double[][2]>(6, {1.0, 0.0});
-
auto p = make_shared<std::vector<int>[]>(4, {1, 2});
template<class T>
shared_ptr<T> make_shared(const remove_extent_t<T>& v);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);
-
- Constraints
-
T
是已知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的extent_v<T>
个对象的序列的shared_ptr
,每个对象都初始化为v
。 - Examples
-
auto p = make_shared<double[1024]>(1.0);
-
auto p = make_shared<double[6][2]>({1.0, 0.0});
-
auto p = make_shared<std::vector<int>[4]>({1, 2});
template<class T>
shared_ptr<T> make_shared_noinit();
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a);
-
- Constraints
-
T
不是数组,或者是已知边界的数组。 - 返回
-
指向类型为
T
的默认初始化对象的shared_ptr
,或分别指向类型为remove_extent_t<T>
的extent_v<T>
个默认初始化对象的序列的shared_ptr
。 - 示例
-
auto p = make_shared_noinit<double[1024]>();
template<class T>
shared_ptr<T> make_shared_noinit(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n);
-
- Constraints
-
T
是未知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的n
个默认初始化对象的序列的shared_ptr
。 - 示例
-
auto p = make_shared_noinit<double[]>(1024);
enable_shared_from_this
描述
类模板 enable_shared_from_this
用作基类,允许从成员函数内部获取指向当前对象的 shared_ptr
或 weak_ptr
。
enable_shared_from_this<T>
定义了两个名为 shared_from_this
的成员函数,它们根据常量性返回指向 this
的 shared_ptr<T>
和 shared_ptr<T const>
。它还定义了两个名为 weak_from_this
的成员函数,它们返回相应的 weak_ptr
。
示例
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>
class Y: public boost::enable_shared_from_this<Y>
{
public:
boost::shared_ptr<Y> f()
{
return shared_from_this();
}
};
int main()
{
boost::shared_ptr<Y> p(new Y);
boost::shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
概要
enable_shared_from_this
在 <boost/smart_ptr/enable_shared_from_this.hpp>
中定义。
namespace boost {
template<class T> class enable_shared_from_this {
private:
// exposition only
weak_ptr<T> weak_this_;
protected:
enable_shared_from_this() = default;
~enable_shared_from_this() = default;
enable_shared_from_this(const enable_shared_from_this&) noexcept;
enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept;
public:
shared_ptr<T> shared_from_this();
shared_ptr<T const> shared_from_this() const;
weak_ptr<T> weak_from_this() noexcept;
weak_ptr<T const> weak_from_this() const noexcept;
}
}
成员
enable_shared_from_this(enable_shared_from_this const &) noexcept;
-
- 效果
-
默认构造
weak_this_
。
注意
|
weak_this_ 不会从参数复制。 |
enable_shared_from_this& operator=(enable_shared_from_this const &) noexcept;
-
- 返回
-
*this
.
注意
|
weak_this_ 未更改。 |
template<class T> shared_ptr<T> shared_from_this();
template<class T> shared_ptr<T const> shared_from_this() const;
-
- 返回
-
shared_ptr<T>(weak_this_)
.
注意
|
当 *this 不由 shared_ptr 拥有时,这些成员会抛出 bad_weak_ptr 。 |
注意
|
当
|
template<class T> weak_ptr<T> weak_from_this() noexcept;
template<class T> weak_ptr<T const> weak_from_this() const noexcept;
-
- 返回
-
weak_this_
.
注意
|
与 shared_from_this() 不同,weak_from_this() 在析构函数中有效,并返回一个 expired() 的 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
那样的 make_unique
实用程序,后者提供了相同的异常安全性和避免编写 new
表达式的功能。在某些标准库供应商实现它之前(以及在 C++14 标准引入 std::make_unique
之前),此库应用户请求提供了它。
此库还为默认初始化提供了 make_unique
的其他重载,当用户不需要或不想承担值初始化的开销时。 C++20 标准现在通过 std::make_unique_for_overwrite
提供了此功能。
概要
make_unique
在 <boost/smart_ptr/make_unique.hpp>
中定义。
namespace boost {
// T is not an array
template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);
// T is not an array
template<class T>
std::unique_ptr<T> make_unique(type_identity_t<T>&& v);
// T is an array of unknown bounds
template<class T>
std::unique_ptr<T> make_unique(std::size_t n);
// T is not an array
template<class T>
std::unique_ptr<T> make_unique_noinit();
// T is an array of unknown bounds
template<class T>
std::unique_ptr<T> make_unique_noinit(std::size_t n);
}
自由函数
template<class T, class... Args>
std::unique_ptr<T> make_unique(Args&&... args);
-
- Constraints
-
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);
-
- Constraints
-
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);
-
- Constraints
-
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();
-
- Constraints
-
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);
-
- Constraints
-
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
,它使用运算符 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>>;
// T is not an array
template<class T, class A, class... Args>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, Args&&... args);
// T is not an array
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, type_identity_t<T>&& v);
// T is an array of unknown bounds
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, std::size_t n);
// T is an array of known bounds
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
allocate_unique(const A& a);
// T is an array of unknown bounds
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);
// T is an array of known bounds
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
allocate_unique(const A& a, const remove_extent_t<T>& v);
// T is not an array
template<class T, class A>
std::unique_ptr<T, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a);
// T is an array of unknown bounds
template<class T, class A>
std::unique_ptr<T, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a, std::size_t n);
// T is an array of known bounds
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a);
template<class T, class U, class A>
allocator_pointer_t<allocator_rebind_t<A, remove_cv_t<remove_extent_t<T>>>>
get_allocator_pointer(const std::unique_ptr<T,
alloc_deleter<U, A>>& p) noexcept;
}
通用要求
除非另有说明,否则适用于所有 allocate_unique
和 allocate_unique_noinit
重载的通用要求如下所述。
- 要求
-
A
应该是分配器。A
的复制构造函数和析构函数不应抛出异常。 - 效果
-
为类型
T
的对象或类型U
的n
个对象(如果T
是U[]
形式的数组类型,并且n
由参数确定,如具体重载所指定)分配内存。对象从参数初始化,如具体重载所指定。使用a
的重新绑定的副本(对于未指定的value_type
)来分配内存。如果抛出异常,则这些函数无效。 - 返回
-
一个
std::unique_ptr
实例,用于存储和拥有新构造对象的地址。 - 后置条件
-
r.get() != 0
,其中r
是返回值。 - 抛出
-
从
A::allocate
或从对象的初始化抛出的异常。 - Remarks
-
-
当指定将数组类型的对象初始化为相同类型
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);
-
- Constraints
-
T
不是数组。 - 返回
-
指向类型为
T
的对象的std::unique_ptr
,从args...
构造。 - Examples
-
auto p = allocate_unique<int>(a);
-
auto p = allocate_unique<std::vector<int>>(a, 16, 1);
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, type_identity_t<T>&& v);
-
- Constraints
-
T
不是数组。 - 返回
-
指向类型为
T
的对象的std::unique_ptr
,从v
构造。 - 示例
-
auto p = allocate_unique<std::vector<int>>(a, {1, 2});
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, std::size_t n);
-
- Constraints
-
T
是未知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的n
个值初始化对象的序列的std::unique_ptr
。 - Examples
-
auto p = allocate_unique<double[]>(a, 1024);
-
auto p = allocate_unique<double[][2][2]>(a, 6);
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
allocate_unique(const A& a);
-
- Constraints
-
T
是已知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的extent_v<T>
个值初始化对象的序列的std::unique_ptr
。 - Examples
-
auto p = allocate_unique<double[1024]>(a);
-
auto p = allocate_unique<double[6][2][2]>(a);
template<class T, class A>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);
-
- Constraints
-
T
是未知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的n
个对象的序列的std::unique_ptr
,每个对象都初始化为v
。 - Examples
-
auto p = allocate_unique<double[]>(a, 1024, 1.0);
-
auto p = allocate_unique<double[][2]>(a, 6, {1.0, 0.0});
-
auto p = allocate_unique<std::vector<int>[]>(a, 4, {1, 2});
template<class T, class A>
std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
allocate_unique(const A& a, const remove_extent_t<T>& v);
-
- Constraints
-
T
是已知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的extent_v<T>
个对象的序列的std::unique_ptr
,每个对象都初始化为v
。 - Examples
-
auto p = allocate_unique<double[1024]>(a, 1.0);
-
auto p = allocate_unique<double[6][2]>(a, {1.0, 0.0});
-
auto p = allocate_unique<std::vector<int>[4]>(a, {1, 2});
template<class T, class A>
std::unique_ptr<T, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a);
-
- Constraints
-
T
不是数组。 - 返回
-
指向类型为
T
的默认初始化对象的std::unique_ptr
。 - 示例
-
auto p = allocate_unique_noinit<double>(a);
template<class T, class A>
std::unique_ptr<T, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a, std::size_t n);
-
- Constraints
-
T
是未知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的n
个默认初始化对象的序列的std::unique_ptr
。 - 示例
-
auto p = allocate_unique_noinit<double[]>(a, 1024);
template<class T, class A>
std::unique_ptr<remove_extent_t<T>, alloc_noinit_deleter<T, A>>
allocate_unique_noinit(const A& a);
-
- Constraints
-
T
是已知边界的数组。 - 返回
-
指向类型为
remove_extent_t<T>
的extent_v<T>
个默认初始化对象的序列的std::unique_ptr
。 - 示例
-
auto p = allocate_unique_noinit<double[1024]>(a);
template<class T, class U, class A>
allocator_pointer_t<allocator_rebind_t<A, remove_cv_t<remove_extent_t<T>>>>
get_allocator_pointer(const std::unique_ptr<T,
alloc_deleter<U, A>>& p) noexcept;
-
- 返回
-
分配的分配器指针。
- 示例
-
auto r = boost::get_allocator_ptr(p);
析构器
类模板 alloc_deleter
是 allocate_unique
函数使用的删除器。
概要
template<class T, class A>
class alloc_deleter {
public:
using pointer =
unspecified
;
explicit alloc_deleter(const A& a) noexcept;
void operator()(pointer p);
};
成员
using pointer =
unspecified
;
-
满足 NullablePointer 的类型。
explicit alloc_deleter(const A& a) noexcept;
-
- 效果
-
从
a
初始化存储的分配器。
void operator()(pointer p);
-
- 效果
-
销毁对象并使用存储的分配器释放
p
引用的存储空间。
intrusive_ptr:管理具有嵌入计数的对象
描述
intrusive_ptr
类模板存储指向具有嵌入式引用计数的对象的指针。每个新的 intrusive_ptr
实例都通过对函数 intrusive_ptr_add_ref
的非限定调用来递增引用计数,并将指针作为参数传递给它。类似地,当 intrusive_ptr
被销毁时,它会调用 intrusive_ptr_release
;此函数负责在对象的引用计数降至零时销毁对象。用户需要提供这两个函数的合适定义。在支持参数依赖查找的编译器上,intrusive_ptr_add_ref
和 intrusive_ptr_release
应在与其参数对应的命名空间中定义;否则,定义需要放在命名空间 boost
中。该库提供了一个辅助基类模板 intrusive_ref_counter
,它可以帮助为用户类型添加对 intrusive_ptr
的支持。
类模板在 T
(指向对象的类型)上参数化。当 T*
可以隐式转换为 U*
时,intrusive_ptr<T>
可以隐式转换为 intrusive_ptr<U>
。
使用 intrusive_ptr
的主要原因是
-
某些现有框架或操作系统提供具有嵌入式引用计数的对象;
-
intrusive_ptr
的内存占用与相应的原始指针相同; -
intrusive_ptr<T>
可以从类型为T*
的任意原始指针构造。
作为一般规则,如果不清楚 intrusive_ptr
是否比 shared_ptr
更适合您的需求,请首先尝试基于 shared_ptr
的设计。
概要
intrusive_ptr
在 <boost/smart_ptr/intrusive_ptr.hpp>
中定义。
namespace boost {
template<class T> class intrusive_ptr {
public:
typedef T element_type;
intrusive_ptr() noexcept;
intrusive_ptr(T * p, bool add_ref = true);
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
~intrusive_ptr();
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
void reset();
void reset(T * r);
void reset(T * r, bool add_ref);
T & operator*() const noexcept;
T * operator->() const noexcept;
T * get() const noexcept;
T * detach() noexcept;
explicit operator bool () const noexcept;
void swap(intrusive_ptr & b) noexcept;
};
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
template<class T>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
intrusive_ptr<Y> const & p);
}
成员
element_type
typedef T element_type;
提供模板参数 T 的类型。
构造函数
intrusive_ptr() noexcept;
-
- 后置条件
-
get() == 0
.
intrusive_ptr(T * p, bool add_ref = true);
-
- 效果
-
if(p != 0 && add_ref) intrusive_ptr_add_ref(p);
. - 后置条件
-
get() == p
.
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
-
- 效果
-
T * p = r.get(); if(p != 0) intrusive_ptr_add_ref(p);
. - 后置条件
-
get() == r.get()
.
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
-
- 后置条件
-
get()
等于r.get()
的旧值。r.get() == 0
。
析构函数
~intrusive_ptr();
-
- 效果
-
if(get() != 0) intrusive_ptr_release(get());
.
赋值
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
-
- 效果
-
等价于
intrusive_ptr(r).swap(*this)
。 - 返回
-
*this
.
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
-
- 效果
-
等价于
intrusive_ptr(std::move(r)).swap(*this)
。 - 返回
-
*this
.
reset
void reset();
-
- 效果
-
等价于
intrusive_ptr().swap(*this)
。
void reset(T * r);
-
- 效果
-
等价于
intrusive_ptr(r).swap(*this)
。
void reset(T * r, bool add_ref);
-
- 效果
-
等价于
intrusive_ptr(r, add_ref).swap(*this)
。
间接引用
T & operator*() const noexcept;
-
- Requirements
-
get() != 0
. - 返回
-
*get()
.
T * operator->() const noexcept;
-
- Requirements
-
get() != 0
. - 返回
-
get()
.
get
T * get() const noexcept;
-
- 返回
-
存储的指针。
detach
T * detach() noexcept;
-
- 返回
-
存储的指针。
- 后置条件
-
get() == 0
.
注意
|
返回的指针具有提升的引用计数。这允许将 intrusive_ptr 转换回原始指针,而不会产生获取和释放额外引用的性能开销。它可以被视为非引用递增构造函数的补充。 |
Caution
|
使用 detach 会逃脱 intrusive_ptr 提供的自动引用计数的安全性。它应该仅在绝对必要时使用(例如在与现有 API 接口时),并且在彻底理解其含义时使用。 |
转换
explicit operator bool () const noexcept;
-
- 返回
-
get() != 0
.
注意
|
此转换运算符允许在布尔上下文中使用 intrusive_ptr 对象,例如 if (p && p->valid()) {} 。 |
注意
|
在 C++03 编译器上,返回值是未指定的类型。 |
swap
void swap(intrusive_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
自由函数
comparison
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
-
- 返回
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
-
- 返回
-
a.get() != b.get()
.
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
-
- 返回
-
a.get() == b
.
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
-
- 返回
-
a.get() != b
.
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
-
- 返回
-
a == b.get()
.
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
-
- 返回
-
a != b.get()
.
template<class T>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
-
- 返回
-
std::less<T *>()(a.get(), b.get())
.
注意
|
允许将 intrusive_ptr 对象用作关联容器中的键。 |
swap
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
-
- 效果
-
等效于
a.swap(b)
。
get_pointer
template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
-
- 返回
-
p.get()
.
注意
|
作为泛型编程的辅助手段提供。供 mem_fn 使用。 |
static_pointer_cast
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- 返回
-
intrusive_ptr<T>(static_cast<T*>(r.get()))
.
const_pointer_cast
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- 返回
-
intrusive_ptr<T>(const_cast<T*>(r.get()))
.
dynamic_pointer_cast
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;
-
- 返回
-
intrusive_ptr<T>(dynamic_cast<T*>(r.get()))
.
operator<<
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
intrusive_ptr<Y> const & p);
-
- 效果
-
os << p.get();
. - 返回
-
os
.
intrusive_ref_counter
描述
intrusive_ref_counter
类模板为派生的用户类实现引用计数器,该用户类旨在与 intrusive_ptr
一起使用。基类具有关联的 intrusive_ptr_add_ref
和 intrusive_ptr_release
函数,它们根据需要修改引用计数器,并在计数器降至零时销毁用户对象。
类模板在 Derived
和 CounterPolicy
参数上参数化。第一个参数是用户从 intrusive_ref_counter
派生的类。需要此类型以便在没有剩余引用时正确销毁对象。
第二个参数是一个策略,用于定义引用计数器的性质。库提供了两个这样的策略:thread_unsafe_counter
和 thread_safe_counter
。前者指示 intrusive_ref_counter
基类使用仅适用于单线程使用的计数器。指向使用此类引用计数器的单个对象的指针不得在不同的线程中使用。后一种策略使引用计数器线程安全,除非目标平台不支持线程。由于现代系统普遍支持线程,因此默认的计数器策略是 thread_safe_counter
。
概要
intrusive_ref_counter
定义在 <boost/smart_ptr/intrusive_ref_counter.hpp>
中。
namespace boost {
struct thread_unsafe_counter;
struct thread_safe_counter;
template<class Derived, class CounterPolicy = thread_safe_counter>
class intrusive_ref_counter {
public:
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter& v) noexcept;
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
unsigned int use_count() const noexcept;
protected:
~intrusive_ref_counter() = default;
};
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
}
成员
构造函数
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter&) noexcept;
-
- 后置条件
-
use_count() == 0
.
注意
|
指向构造对象的指针应传递给 intrusive_ptr 构造函数、赋值运算符或 reset 方法,这将增加引用计数器。 |
析构函数
~intrusive_ref_counter();
-
- 效果
-
销毁计数器对象。
注意
|
析构函数是受保护的,因此对象只能通过 Derived 类销毁。 |
赋值
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
-
- 效果
-
不执行任何操作,引用计数器不会被修改。
use_count
unsigned int use_count() const noexcept;
-
- 返回
-
引用计数器的当前值。
注意
|
在多线程应用程序中,返回的值可能不是实际值。 |
自由函数
intrusive_ptr_add_ref
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- 效果
-
增加引用计数器。
intrusive_ptr_release
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- 效果
-
减少引用计数器。如果引用计数器达到 0,则调用
delete static_cast<const Derived*>(p)
。
local_shared_ptr:单线程内的共享所有权
描述
local_shared_ptr
几乎与 shared_ptr
完全相同,唯一的显著区别是它的引用计数使用非原子操作更新。因此,local_shared_ptr
及其所有副本必须驻留在(本地于)单个线程中(因此得名)。
local_shared_ptr
可以转换为 shared_ptr
,反之亦然。从 shared_ptr
创建 local_shared_ptr
会创建一个新的本地引用计数;这意味着两个 local_shared_ptr
实例,都从同一个 shared_ptr
创建,引用同一个对象,但不共享同一个计数,因此可以安全地被两个不同的线程使用。
shared_ptr<X> p1( new X );
local_shared_ptr<X> p2( p1 ); // p2.local_use_count() == 1
local_shared_ptr<X> p3( p1 ); // p3.local_use_count() also 1
但是,从第一个 local_shared_ptr
创建第二个 local_shared_ptr
会导致两者共享同一个计数
shared_ptr<X> p1( new X );
local_shared_ptr<X> p2( p1 ); // p2.local_use_count() == 1
local_shared_ptr<X> p3( p2 ); // p3.local_use_count() == 2
从同一个 local_shared_ptr
创建的两个 shared_ptr
实例确实共享所有权
local_shared_ptr<X> p1( new X );
shared_ptr<X> p2( p1 ); // p2.use_count() == 2
shared_ptr<X> p3( p1 ); // p3.use_count() == 3
这里 p2.use_count()
是 2,因为 p1
也持有一个引用。
可以将 local_shared_ptr<T>
视为 shared_ptr<shared_ptr<T>>
,其中外部 shared_ptr
对其计数使用非原子操作。从 local_shared_ptr
转换为 shared_ptr
会为您提供内部 shared_ptr
的副本;从 shared_ptr
转换会将其包装到具有非原子使用计数的外部 shared_ptr
中(概念上来说),并返回结果。
概要
local_shared_ptr
定义在 <boost/smart_ptr/local_shared_ptr.hpp>
中。
namespace boost {
template<class T> class local_shared_ptr {
public:
typedef /*see below*/ element_type;
// constructors
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
template<class Y> explicit local_shared_ptr(Y * p);
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
// destructor
~local_shared_ptr() noexcept;
// assignment
local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
local_shared_ptr & operator=(local_shared_ptr const && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
local_shared_ptr & operator=(std::nullptr_t) noexcept;
// reset
void reset() noexcept;
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
// accessors
T & operator*() const noexcept; // only valid when T is not an array type
T * operator->() const noexcept; // only valid when T is not an array type
// only valid when T is an array type
element_type & operator[](std::ptrdiff_t i) const noexcept;
element_type * get() const noexcept;
long local_use_count() const noexcept;
// conversions
explicit operator bool() const noexcept;
template<class Y> operator shared_ptr<Y>() const noexcept;
template<class Y> operator weak_ptr<Y>() const noexcept;
// swap
void swap(local_shared_ptr & b) noexcept;
// owner_before
template<class Y> bool owner_before(local_shared_ptr<Y> const & r) const noexcept;
// owner_equals
template<class Y> bool owner_equals(local_shared_ptr<Y> const & r) const noexcept;
};
// comparisons
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
// swap
template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
// get_pointer
template<class T>
typename local_shared_ptr<T>::element_type *
get_pointer(local_shared_ptr<T> const & p) noexcept;
// casts
template<class T, class U>
local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> const_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> dynamic_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> reinterpret_pointer_cast(local_shared_ptr<U> const & r) noexcept;
// stream I/O
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, local_shared_ptr<Y> const & p);
// get_deleter
template<class D, class T> D * get_deleter(local_shared_ptr<T> const & p) noexcept;
}
成员
element_type
typedef ... element_type;
当 T
不是数组类型时,element_type
为 T
,当 T
为 U[]
或 U[N]
时,element_type
为 U
。
默认构造函数
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
-
- 效果
-
构造一个空的
local_shared_ptr
。 - 后置条件
-
local_use_count() == 0 && get() == 0
.
指针构造函数
template<class Y> explicit local_shared_ptr(Y * p);
-
- 效果
-
构造一个拥有
shared_ptr<T>( p )
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1 && get() == p
. - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。
接受析构函数的构造函数
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
-
- 效果
-
构造一个拥有
shared_ptr<T>( p, d )
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1 && get() == p
. - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。
template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
-
- 效果
-
构造一个拥有
shared_ptr<T>( p, d, a )
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1 && get() == p
. - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。
复制和转换构造函数
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
-
- 要求
-
Y*
应该可转换为T*
。 - 效果
-
如果
r
为空,则构造一个空的local_shared_ptr
;否则,构造一个与r
共享所有权的local_shared_ptr
。 - 后置条件
-
get() == r.get() && local_use_count() == r.local_use_count()
.
移动构造函数
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
-
- 要求
-
Y*
应该可转换为T*
。 - 效果
-
从
r
移动构造一个local_shared_ptr
。 - 后置条件
-
*this
包含r
的旧值。r
为空且r.get() == 0
。
shared_ptr 构造函数
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
-
- 效果
-
构造一个拥有
r
的local_shared_ptr
。 - 后置条件
-
local_use_count() == 1
。get()
返回r.get()
的旧值。 - 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。
别名构造函数
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
构造一个与
r
共享所有权并存储p
的local_shared_ptr
。 - 后置条件
-
get() == p && local_use_count() == r.local_use_count()
.
别名移动构造函数
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
从
r
移动构造一个local_shared_ptr
,同时存储p
代替。 - 后置条件
-
get() == p
且local_use_count()
等于r
的旧计数。r
为空且r.get() == 0
。
unique_ptr 构造函数
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
-
- 要求
-
Y*
应该可转换为T*
。 - 效果
-
-
当
r.get() == 0
时,等效于local_shared_ptr()
; -
否则,构造一个拥有
shared_ptr<T>( std::move(r) )
的local_shared_ptr
。
-
- 抛出
-
std::bad_alloc
,或当无法获得内存以外的资源时,实现定义的异常。 - 异常安全性
-
如果抛出异常,则构造函数不起作用。
析构函数
~local_shared_ptr() noexcept;
-
- 效果
-
-
如果
*this
为空,或与另一个local_shared_ptr
实例共享所有权 (local_use_count() > 1
),则没有副作用。 -
否则,销毁拥有的
shared_ptr
。
-
赋值
local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
-
- 效果
-
等效于
local_shared_ptr(r).swap(*this)
。 - 返回
-
*this
.
local_shared_ptr & operator=(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- 效果
-
等效于
local_shared_ptr(std::move(r)).swap(*this)
。 - 返回
-
*this
.
local_shared_ptr & operator=(std::nullptr_t) noexcept;
-
- 效果
-
等效于
local_shared_ptr().swap(*this)
。 - 返回
-
*this
.
reset
void reset() noexcept;
-
- 效果
-
等效于
local_shared_ptr().swap(*this)
。
template<class Y> void reset(Y * p);
-
- 效果
-
等效于
local_shared_ptr(p).swap(*this)
。
template<class Y, class D> void reset(Y * p, D d);
-
- 效果
-
等效于
local_shared_ptr(p, d).swap(*this)
。
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- 效果
-
等效于
local_shared_ptr(p, d, a).swap(*this)
。
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
等效于
local_shared_ptr(r, p).swap(*this)
。
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
等效于
local_shared_ptr(std::move(r), p).swap(*this)
。
间接引用
T & operator*() const noexcept;
-
- 要求
-
T
不应为数组类型。 - 返回
-
*get()
.
T * operator->() const noexcept;
-
- 要求
-
T
不应为数组类型。 - 返回
-
get()
.
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- 要求
-
T
应为数组类型。 存储的指针不得为 0。i >= 0
。 如果T
为U[N]
,则i < N
。 - 返回
-
get()[i]
.
get
element_type * get() const noexcept;
-
- 返回
-
存储的指针。
local_use_count
long local_use_count() const noexcept;
-
- 返回
-
与
*this
共享所有权的local_shared_ptr
对象的数量,包括*this
,或者当*this
为空时为 0。
转换
explicit operator bool() const noexcept;
-
- 返回
-
get() != 0
.
注意
|
在 C++03 编译器上,返回值是未指定的类型。 |
template<class Y> operator shared_ptr<Y>() const noexcept;
template<class Y> operator weak_ptr<Y>() const noexcept;
-
- 要求
-
T*
应该可以转换为Y*
。 - 返回
-
拥有的
shared_ptr
的副本。
swap
void swap(local_shared_ptr & b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
owner_before
template<class Y> bool owner_before(local_shared_ptr<Y> const & r) const noexcept;
-
- 返回
-
请参阅
operator<
的描述。
owner_equals
template<class Y> bool owner_equals(local_shared_ptr<Y> const & r) const noexcept;
-
- 返回
-
true
,当且仅当*this
和r
共享所有权或两者都为空时。
自由函数
comparison
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 返回
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 返回
-
a.get() != b.get()
.
template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
-
- 返回
-
p.get() == 0
.
template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
-
- 返回
-
p.get() != 0
.
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 返回
-
一个未指定的值,使得
-
operator<
是 C++ 标准 [lib.alg.sorting] 节中描述的严格弱序关系; -
在由
operator<
定义的等价关系下,!(a < b) && !(b < a)
,两个local_shared_ptr
实例是等价的,当且仅当它们共享所有权或都为空时。
-
注意
|
允许 local_shared_ptr 对象用作关联容器中的键。 |
注意
|
出于设计考虑,省略了其余的比较运算符。 |
swap
template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
-
- 效果
-
等效于
a.swap(b)
。
get_pointer
template<class T>
typename local_shared_ptr<T>::element_type *
get_pointer(local_shared_ptr<T> const & p) noexcept;
-
- 返回
-
p.get()
.
注意
|
作为泛型编程的辅助手段提供。供 mem_fn 使用。 |
static_pointer_cast
template<class T, class U>
local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
-
- 要求
-
表达式
static_cast<T*>( (U*)0 )
必须是良构的。 - 返回
-
local_shared_ptr<T>( r, static_cast<typename local_shared_ptr<T>::element_type*>(r.get()) )
.
Caution
|
看似等效的表达式 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 {
// T is not an array
template<class T, class... Args>
local_shared_ptr<T> make_local_shared(Args&&... args);
template<class T, class A, class... Args>
local_shared_ptr<T> allocate_local_shared(const A& a, Args&&... args);
// T is an array of unknown bounds
template<class T>
local_shared_ptr<T> make_local_shared(std::size_t n);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n);
// T is an array of known bounds
template<class T>
local_shared_ptr<T> make_local_shared();
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a);
// T is an array of unknown bounds
template<class T>
local_shared_ptr<T> make_local_shared(std::size_t n,
const remove_extent_t<T>& v);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n,
const remove_extent_t<T>& v);
// T is an array of known bounds
template<class T>
local_shared_ptr<T> make_local_shared(const remove_extent_t<T>& v);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a,
const remove_extent_t<T>& v);
// T is not an array of known bounds
template<class T>
local_shared_ptr<T> make_local_shared_noinit();
template<class T, class A>
local_shared_ptr<T> allocate_local_shared_noinit(const A& a);
// T is an array of unknown bounds
template<class T>
local_shared_ptr<T> make_local_shared_noinit(std::size_t n);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared_noinit(const A& a,
std::size_t n);
}
描述
这些函数的要求和效果与 make_shared
和 allocate_shared
相同,只是返回的是 local_shared_ptr
。
通用指针转换
描述
指针转换函数模板(static_pointer_cast
、dynamic_pointer_cast
、const_pointer_cast
和 reinterpret_pointer_cast
)为原始指针、std::shared_ptr
和 std::unique_ptr
提供了一种编写通用指针转换的方法。
在 pointer_cast_test.cpp 中有测试和示例代码
理由
Boost 智能指针通常重载这些函数以提供模拟指针转换的机制。例如,shared_ptr<T>
以这种方式实现静态指针转换
template<class T, class U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
指针转换函数模板是 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()))
.
Caution
|
看似等效的表达式 std::unique_ptr<T>(static_cast<T*>(p.get())) 最终将导致未定义的行为,试图删除同一个对象两次。 |
dynamic_pointer_cast
template<class T, class U> T* dynamic_pointer_cast(U* p) noexcept;
-
- 返回
-
dynamic_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
dynamic_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回
-
std::dynamic_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
dynamic_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
static_cast<T*>((U*)0)
必须格式良好。 -
T
必须具有虚析构函数。- 返回
-
当
dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.get())
返回非零值时,std::unique_ptr<T>(dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.release()));
。 -
否则,
std::unique_ptr<T>()
。
const_pointer_cast
template<class T, class U> T* const_pointer_cast(U* p) noexcept;
-
- 返回
-
const_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
const_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回
-
std::const_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
const_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
const_cast<T*>((U*)0)
必须格式良好。 - 返回
-
std::unique_ptr<T>(const_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
reinterpret_pointer_cast
template<class T, class U> T* reinterpret_pointer_cast(U* p) noexcept;
-
- 返回
-
reinterpret_cast<T*>(p)
template<class T, class U> std::shared_ptr<T>
reinterpret_pointer_cast(const std::shared_ptr<U>& p) noexcept;
-
- 返回
-
std::reinterpret_pointer_cast<T>(p)
template<class T, class U> std::unique_ptr<T>
reinterpret_pointer_cast(std::unique_ptr<U>&& p) noexcept;
-
- 要求
-
表达式
reinterpret_cast<T*>((U*)0)
必须格式良好。 - 返回
-
std::unique_ptr<T>(reinterpret_cast<typename std::unique_ptr<T>::element_type*>(p.release()))
.
示例
以下示例演示了通用指针转换如何帮助我们创建独立于指针类型的代码。
#include <boost/pointer_cast.hpp>
#include <boost/shared_ptr.hpp>
class base {
public:
virtual ~base() { }
};
class derived : public base { };
template<class Ptr>
void check_if_it_is_derived(const Ptr& ptr)
{
assert(boost::dynamic_pointer_cast<derived>(ptr) != 0);
}
int main()
{
base* ptr = new derived;
boost::shared_ptr<base> sptr(new derived);
check_if_it_is_derived(ptr);
check_if_it_is_derived(sptr);
delete ptr;
}
pointer_to_other
描述
pointer_to_other
实用程序提供了一种方法,给定源指针类型,获取指向另一个被指向类型的相同类型的指针。
在 pointer_to_other_test.cpp 中有测试/示例代码。
理由
在构建独立于指针类型的类(如内存管理器、分配器或容器)时,通常需要通用地定义指针,以便如果模板参数表示指针(例如,指向 int
的原始指针或智能指针),我们可以定义指向另一个被指向类型的相同类型的另一个指针(指向 float
的原始指针或智能指针)。
template <class IntPtr> class FloatPointerHolder
{
// Let's define a pointer to a float
typedef typename boost::pointer_to_other
<IntPtr, float>::type float_ptr_t;
float_ptr_t float_ptr;
};
概要
pointer_to_other
定义在 <boost/smart_ptr/pointer_to_other.hpp>
中。
namespace boost {
template<class T, class U> struct pointer_to_other;
template<class T, class U,
template <class> class Sp>
struct pointer_to_other< Sp<T>, U >
{
typedef Sp<U> type;
};
template<class T, class T2, class U,
template <class, class> class Sp>
struct pointer_to_other< Sp<T, T2>, U >
{
typedef Sp<U, T2> type;
};
template<class T, class T2, class T3, class U,
template <class, class, class> class Sp>
struct pointer_to_other< Sp<T, T2, T3>, U >
{
typedef Sp<U, T2, T3> type;
};
template<class T, class U>
struct pointer_to_other< T*, U >
{
typedef U* type;
};
}
如果这些定义对于特定的智能指针不正确,我们可以定义 pointer_to_other
的特化。
示例
// Let's define a memory allocator that can
// work with raw and smart pointers
#include <boost/pointer_to_other.hpp>
template <class VoidPtr>
class memory_allocator
{
// Predefine a memory_block
struct block;
// Define a pointer to a memory_block from a void pointer
// If VoidPtr is void *, block_ptr_t is block*
// If VoidPtr is smart_ptr<void>, block_ptr_t is smart_ptr<block>
typedef typename boost::pointer_to_other
<VoidPtr, block>::type block_ptr_t;
struct block
{
std::size_t size;
block_ptr_t next_block;
};
block_ptr_t free_blocks;
};
正如我们所见,使用 pointer_to_other
我们可以创建独立于指针类型的代码。
atomic_shared_ptr
描述
类模板 atomic_shared_ptr<T>
为类型为 shared_ptr<T>
的包含值实现了 std::atomic
的接口。并发访问 atomic_shared_ptr
不是数据竞争。
概要
atomic_shared_ptr
定义在 <boost/smart_ptr/atomic_shared_ptr.hpp>
中。
namespace boost {
template<class T> class atomic_shared_ptr {
private:
shared_ptr<T> p_; // exposition only
atomic_shared_ptr(const atomic_shared_ptr&) = delete;
atomic_shared_ptr& operator=(const atomic_shared_ptr&) = delete;
public:
constexpr atomic_shared_ptr() noexcept;
atomic_shared_ptr( shared_ptr<T> p ) noexcept;
atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;
bool is_lock_free() const noexcept;
shared_ptr<T> load( int = 0 ) const noexcept;
operator shared_ptr<T>() const noexcept;
void store( shared_ptr<T> r, int = 0 ) noexcept;
shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
};
}
成员
constexpr atomic_shared_ptr() noexcept;
-
- 效果
-
默认初始化
p_
。
atomic_shared_ptr( shared_ptr<T> p ) noexcept;
-
- 效果
-
将
p_
初始化为p
。
atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;
-
- 效果
-
p_.swap(r)
. - 返回
-
*this
.
bool is_lock_free() const noexcept;
-
- 返回
-
false
.
注意
|
此实现不是无锁的。 |
shared_ptr<T> load( int = 0 ) const noexcept;
operator shared_ptr<T>() const noexcept;
-
- 返回
-
p_
.
注意
|
int 参数旨在为 memory_order 类型,但被忽略。此实现是基于锁的,因此始终是顺序一致的。 |
void store( shared_ptr<T> r, int = 0 ) noexcept;
-
- 效果
-
p_.swap(r)
.
shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;
-
- 效果
-
p_.swap(r)
. - 返回
-
p_
的旧值。
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
-
- 效果
-
如果
p_
等效于v
,则将w
分配给p_
,否则将p_
分配给v
。 - 返回
-
如果
p_
等效于v
,则为true
,否则为false
。 - Remarks
-
如果两个
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
。 - Remarks
-
w
的旧值在任何情况下都不会保留。
owner_less
描述
owner_less<T>
是一个辅助函数对象,它使用 owner_before
比较两个智能指针对象。它仅为与 C++11 的兼容性而提供,并且对应于同名的标准组件。
当使用 Boost 智能指针时,owner_less
的使用是不必要的,因为提供的 operator<
重载(以及相应的 std::less
)返回相同的结果。
概要
owner_less
定义在 <boost/smart_ptr/owner_less.hpp>
中。
namespace boost {
template<class T = void> struct owner_less
{
typedef bool result_type;
typedef T first_argument_type;
typedef T second_argument_type;
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
};
}
成员
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
-
- 返回
-
u.owner_before( v )
.
owner_equal_to
描述
owner_equal_to<T>
是一个辅助函数对象,它使用 owner_equals
比较两个智能指针对象。
概要
owner_equal_to
定义在 <boost/smart_ptr/owner_equal_to.hpp>
中。
namespace boost {
template<class T = void> struct owner_equal_to
{
typedef bool result_type;
typedef T first_argument_type;
typedef T second_argument_type;
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
};
}
成员
template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
-
- 返回
-
u.owner_equals( v )
.
owner_hash
描述
owner_hash<T>
是一个辅助函数对象,它接受一个智能指针 p
并返回 p.owner_hash_value()
。它对于创建使用基于所有权的相等性而不是默认指针值相等性的 shared_ptr
的无序容器非常有用。(它也可以与 weak_ptr
一起使用,但没有必要,因为 boost::hash
和 std::hash
对于 weak_ptr
已经使用基于所有权的相等性。)
示例
std::unordered_set< boost::shared_ptr<void>,
boost::owner_hash< boost::shared_ptr<void> >,
boost::owner_equal_to< boost::shared_ptr<void> > > set;
概要
owner_hash
定义在 <boost/smart_ptr/owner_hash.hpp>
中。
namespace boost {
template<class T> struct owner_hash
{
typedef std::size_t result_type;
typedef T argument_type;
std::size_t operator()( T const & p ) const noexcept;
};
}
成员
std::size_t operator()( T const & p ) const noexcept;
-
- 返回
-
p.owner_hash_value()
.
附录 A:智能指针编程技术
使用不完整类进行实现隐藏
一种经过验证的技术(也适用于 C 语言),用于将接口与实现分离,是使用指向不完整类的指针作为不透明句柄
class FILE;
FILE * fopen(char const * name, char const * mode);
void fread(FILE * f, void * data, size_t size);
void fclose(FILE * f);
可以使用 shared_ptr
来表达上述接口,从而无需手动调用 fclose
class FILE;
shared_ptr<FILE> fopen(char const * name, char const * mode);
void fread(shared_ptr<FILE> f, void * data, size_t size);
此技术依赖于 shared_ptr
执行自定义删除器的能力,消除了对 fclose
的显式调用,以及 shared_ptr<X>
可以在 X
不完整时复制和销毁的事实。
“Pimpl” 惯用法
不完整类模式的 C++ 特定变体是 "Pimpl" 惯用法。不完整类不向用户公开;它隐藏在转发外观后面。shared_ptr
可以用于实现 "Pimpl"
// file.hpp:
class file
{
private:
class impl;
shared_ptr<impl> pimpl_;
public:
file(char const * name, char const * mode);
// compiler generated members are fine and useful
void read(void * data, size_t size);
};
// file.cpp:
#include "file.hpp"
class file::impl
{
private:
impl(impl const &);
impl & operator=(impl const &);
// private data
public:
impl(char const * name, char const * mode) { ... }
~impl() { ... }
void read(void * data, size_t size) { ... }
};
file::file(char const * name, char const * mode): pimpl_(new impl(name, mode))
{
}
void file::read(void * data, size_t size)
{
pimpl_->read(data, size);
}
这里需要注意的关键是,编译器生成的复制构造函数、赋值运算符和析构函数都具有合理的含义。因此,file
是 CopyConstructible
和 Assignable
,允许其在标准容器中使用。
使用抽象类进行实现隐藏
另一种广泛使用的 C++ 惯用法,用于分离接口和实现,是使用抽象基类和工厂函数。抽象类有时称为“接口”,该模式称为“基于接口的编程”。同样,shared_ptr
可以用作工厂函数的返回类型
// X.hpp:
class X
{
public:
virtual void f() = 0;
virtual void g() = 0;
protected:
~X() {}
};
shared_ptr<X> createX();
// X.cpp:
class X_impl: public X
{
private:
X_impl(X_impl const &);
X_impl & operator=(X_impl const &);
public:
virtual void f()
{
// ...
}
virtual void g()
{
// ...
}
};
shared_ptr<X> createX()
{
shared_ptr<X> px(new X_impl);
return px;
}
shared_ptr
的一个关键属性是,分配、构造、反分配和销毁细节在构造点,即工厂函数内部捕获。
请注意上面示例中的受保护和非虚拟析构函数。客户端代码不能,也不需要,删除指向 X
的指针;从 createX
返回的 shared_ptr<X>
实例将正确调用 ~X_impl
。
防止 delete px.get()
通常希望阻止客户端代码删除由 shared_ptr
管理的指针。先前的技术展示了一种可能的方法,即使用受保护的析构函数。另一种替代方法是使用私有删除器
class X
{
private:
~X();
class deleter;
friend class deleter;
class deleter
{
public:
void operator()(X * p) { delete p; }
};
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X, X::deleter());
return px;
}
};
封装分配细节,包装工厂函数
shared_ptr
可以用于创建 C++ 包装器,包装现有 C 风格库接口,这些接口从其工厂函数返回原始指针以封装分配细节。作为一个例子,考虑这个接口,其中 CreateX
可能会从其自己的私有堆分配 X
,~X
可能无法访问,或者 X
可能是不完整的
X * CreateX(); void DestroyX(X *);
可靠销毁 CreateX
返回的指针的唯一方法是调用 DestroyX
。
这是一个基于 shared_ptr
的包装器的示例
shared_ptr<X> createX() { shared_ptr<X> px(CreateX(), DestroyX); return px; }
调用 createX
的客户端代码仍然不需要知道对象是如何分配的,但现在销毁是自动的。
使用 shared_ptr 来保存指向静态分配对象的指针
有时希望为已存在的对象创建一个 shared_ptr
,以便在没有更多引用留下时,shared_ptr
不会尝试销毁该对象。例如,工厂函数
shared_ptr<X> createX();
在某些情况下可能需要返回指向静态分配的 X
实例的指针。
解决方案是使用一个什么都不做的自定义删除器
struct null_deleter
{
void operator()(void const *) const
{
}
};
static X x;
shared_ptr<X> createX()
{
shared_ptr<X> px(&x, null_deleter());
return px;
}
相同的技术适用于任何已知比指针寿命更长的对象。
使用 shared_ptr 来保存指向 COM 对象的指针
背景:COM 对象具有嵌入式引用计数和两个操作它的成员函数。AddRef()
增加计数。Release()
减少计数,并在计数降至零时销毁自身。
可以将指向 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
中创建的单个引用。当最后一个 shared_ptr
被销毁时,从 pw
创建的弱指针将失效,而不管 COM 对象本身是否仍然存活。
正如 mem_fn
文档中 解释 的那样,您需要首先 #define BOOST_MEM_FN_ENABLE_STDCALL
。
使用 shared_ptr 来保存指向具有嵌入引用计数的对象的指针
这是上述技术的推广。该示例假设对象实现了 intrusive_ptr
所需的两个函数,intrusive_ptr_add_ref
和 intrusive_ptr_release
template<class T> struct intrusive_deleter
{
void operator()(T * p)
{
if(p) intrusive_ptr_release(p);
}
};
shared_ptr<X> make_shared_from_intrusive(X * p)
{
if(p) intrusive_ptr_add_ref(p);
shared_ptr<X> px(p, intrusive_deleter<X>());
return px;
}
使用 shared_ptr 来保存另一个共享所有权智能指针
shared_ptr
的设计目标之一是在库接口中使用它。可能会遇到库接受 shared_ptr
参数的情况,但手头的对象由不同的引用计数或链接的智能指针管理。
可以利用 shared_ptr
的自定义删除器功能,将此现有智能指针包装在 shared_ptr
外观之后
template<class P> struct smart_pointer_deleter
{
private:
P p_;
public:
smart_pointer_deleter(P const & p): p_(p)
{
}
void operator()(void const *)
{
p_.reset();
}
P const & get() const
{
return p_;
}
};
shared_ptr<X> make_shared_from_another(another_ptr<X> qx)
{
shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx));
return px;
}
一个微妙的点是,不允许删除器抛出异常,并且上面编写的示例假设 p_.reset()
不会抛出异常。如果不是这种情况,p_.reset();
应该包装在忽略异常的 try {} catch(…) {}
块中。在异常被抛出并忽略的(通常不太可能发生的)事件中,当删除器的生命周期结束时,p_
将被释放。当所有引用(包括弱指针)都被销毁或重置时,会发生这种情况。
另一个转折是,给定上述 shared_ptr
实例,可以使用 get_deleter
恢复原始智能指针
void extract_another_from_shared(shared_ptr<X> px)
{
typedef smart_pointer_deleter< another_ptr<X> > deleter;
if(deleter const * pd = get_deleter<deleter>(px))
{
another_ptr<X> qx = pd->get();
}
else
{
// not one of ours
}
}
从原始指针获取 shared_ptr
有时,给定指向已由另一个 shared_ptr
实例管理的对象的原始指针,需要获取 shared_ptr
。例子
void f(X * p) { shared_ptr<X> px(???); }
在 f
内部,我们想创建一个指向 *p
的 shared_ptr
。
在一般情况下,这个问题没有解决方案。一种方法是修改 f
以接受 shared_ptr
,如果可能的话
void f(shared_ptr<X> px);
相同的转换可以用于非虚拟成员函数,以转换隐式的 this
void X::f(int m);
将变成一个以 shared_ptr
为第一个参数的自由函数
void f(shared_ptr<X> this_, int m);
如果 f
无法更改,但 X
使用侵入式计数,请使用上面描述的 make_shared_from_intrusive
。或者,如果已知在 f
中创建的 shared_ptr
永远不会比对象寿命更长,请使用 空删除器。
在构造函数中获取指向 this 的 shared_ptr (weak_ptr)
某些设计要求对象在构造时向中央机构注册自身。当注册例程接受 shared_ptr
时,这引出了构造函数如何获取指向 this
的 shared_ptr
的问题
class X
{
public:
X()
{
shared_ptr<X> this_(???);
}
};
在一般情况下,这个问题无法解决。正在构造的 X
实例可以是自动变量或静态变量;它可以在堆上创建
shared_ptr<X> px(new X);
但在构造时,px
尚不存在,并且无法创建另一个与其共享所有权的 shared_ptr
实例。
根据上下文,如果内部 shared_ptr this_
不需要保持对象存活,请使用 此处 和 此处 解释的 null_deleter
。如果 X
应该始终存在于堆上,并由 shared_ptr
管理,请使用静态工厂函数
class X
{
private:
X() { ... }
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X);
// use px as 'this_'
return px;
}
};
获取指向 this 的 shared_ptr
有时需要在虚成员函数中从 this
获取 shared_ptr
,假设 this
已经由 shared_ptr
管理。 先前技术中描述的转换 无法应用。
一个典型的例子
class X
{
public:
virtual void f() = 0;
protected:
~X() {}
};
class Y
{
public:
virtual shared_ptr<X> getX() = 0;
protected:
~Y() {}
};
// --
class impl: public X, public Y
{
public:
impl() { ... }
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
shared_ptr<X> px(???);
return px;
}
};
解决方案是将指向 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>
实例
shared_ptr<void> pv(new X);
被销毁时,它将通过执行 ~X
正确处理 X
对象。
此属性的使用方式与原始 void*
用于暂时剥离对象指针的类型信息的方式非常相似。shared_ptr<void>
稍后可以使用 static_pointer_cast
转换回正确的类型。
将任意数据与异构 shared_ptr
实例关联
shared_ptr
和 weak_ptr
支持标准关联容器(如 std::map
)所需的 operator<
比较。这可以用于非侵入式地将任意数据与由 shared_ptr
管理的对象关联起来
typedef int Data;
std::map<shared_ptr<void>, Data> userData;
// or std::map<weak_ptr<void>, Data> userData; to not affect the lifetime
shared_ptr<X> px(new X);
shared_ptr<int> pi(new int(3));
userData[px] = 42;
userData[pi] = 91;
使用 shared_ptr
作为 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 的文章“包装 C++ 成员函数调用”(在线提供于 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
包含指向辅助对象的指针,辅助对象又包含指向对象的指针和计数。 -
嵌入式附加:计数是指向对象的一个成员。
-
放置附加:计数通过运算符 new 操作附加。
每种实现技术都有优点和缺点。我们甚至对直接和间接方法进行了各种计时,发现至少在 Intel Pentium 芯片上,可测量的差异非常小。Kevlin Henney 提供了一篇他写的关于“计数主体技术”的论文。Dietmar Kühl 建议了一种优雅的部分模板特化技术,允许用户选择他们喜欢的实现,并且也对此进行了实验。
但 Greg Colvin 和 Jerry Schwarz 认为“参数化会阻止用户”,最终我们选择仅提供直接实现。
1999 年 5 月
在 1999 年 4 月和 5 月,Valentin Bonnard 和 David Abrahams 提出了许多建议,从而进行了许多改进。
1999 年 9 月
Luis Coelho 提供了 shared_ptr::swap
和 shared_array::swap
。
1999 年 11 月
Darin Adler 为共享类型提供了 operator ==
、operator !=
以及 std::swap
和 std::less
特化。
2001 年 5 月
Vladimir Prus 建议在销毁时需要完整类型。改进是在包括 Dave Abrahams、Greg Colvin、Beman Dawes、Rainer Deyke、Peter Dimov、John Maddock、Vladimir Prus、Shankar Sai 等的讨论中演变而来的。
2002 年 1 月
Peter Dimov 重做了所有四个类,添加了功能,修复了错误,将它们拆分为四个单独的头文件,并添加了 weak_ptr
。
2003 年 3 月
Peter Dimov、Beman Dawes 和 Greg Colvin 提议 将 shared_ptr
和 weak_ptr
包含在标准库中,通过第一个库技术报告(称为 TR1)。该提案被接受,并最终成为 2011 年迭代的 C++ 标准的一部分。
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
上参数化,T
是指向的对象的类型。shared_array
及其大多数成员函数对 T
没有要求;它允许是不完整类型或 void
。对成员函数提出额外要求(构造函数、reset)的在下面明确记录。
概要
namespace boost {
template<class T> class shared_array {
public:
typedef T element_type;
explicit shared_array(T* p = 0);
template<class D> shared_array(T* p, D d);
shared_array(const shared_array& v) noexcept;
~shared_array() noexcept;
shared_array& operator=(const shared_array& v) noexcept;
void reset(T* p = 0);
template<class D> void reset(T* p, D d);
T& operator[](std::ptrdiff_t n) const noexcept;
T* get() const noexcept;
bool unique() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
void swap(shared_array<T>& v) noexcept;
};
template<class T> bool
operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
}
成员
element_type
typedef T element_type;
- 类型
-
提供存储指针的类型。
构造函数
explicit shared_array(T* p = 0);
-
- 效果
-
构造一个
shared_array
,存储p
的副本,p
必须是指向通过 C++new[]
表达式分配的数组的指针或为 0。之后,使用计数为 1(即使p == 0
;请参阅~shared_array
)。 - 要求
-
T
是一个完整类型。 - 抛出
-
std::bad_alloc
。如果抛出异常,则调用delete[] p
。
template<class D> shared_array(T* p, D d);
-
- 效果
-
构造一个
shared_array
,存储p
和d
的副本。之后,使用计数为 1。当删除p
指向的数组时,对象d
在语句d(p)
中使用。 - 要求
-
T
是一个完整类型。 -
D
的复制构造函数和析构函数不得抛出异常。 -
使用参数
p
调用对象d
不得抛出异常。- 抛出
-
std::bad_alloc
。如果抛出异常,则调用d(p)
。
shared_array(const shared_array& v) noexcept;
-
- 效果
-
构造一个
shared_array
,就像存储v
中存储的指针的副本一样。之后,所有副本的使用计数比初始使用计数多 1。 - 要求
-
T
是一个完整类型。
析构函数
~shared_array() noexcept;
-
- 效果
-
减少使用计数。然后,如果使用计数为 0,则删除存储的指针指向的数组。请注意,对值为 0 的指针执行
delete[]
是无害的。
赋值
shared_array& operator=(const shared_array& v) noexcept;
-
- 效果
-
构造一个新的如上所述的
shared_array
,然后用新的shared_array
替换此shared_array
,从而销毁被替换的对象。 - 要求
-
T
是一个完整类型。 - 返回
-
*this
.
reset
void reset(T* p = 0);
-
- 效果
-
构造一个新的如上所述的
shared_array
,然后用新的shared_array
替换此shared_array
,从而销毁被替换的对象。 - 要求
-
T
是一个完整类型。 - 抛出
-
std::bad_alloc
。如果抛出异常,则调用delete[] p
。
template<class D> void reset(T* p, D d);
-
- 效果
-
构造一个新的如上所述的
shared_array
,然后用新的shared_array
替换此shared_array
,从而销毁被替换的对象。 - 要求
-
T
是一个完整类型。 -
D
的复制构造函数不得抛出异常。- 抛出
-
std::bad_alloc
。如果抛出异常,则调用d(p)
。
索引
T& operator[](std::ptrdiff_t n) const noexcept;
- 返回
-
对存储的指针指向的数组的元素
n
的引用。如果存储的指针为 0,或者如果n
小于 0 或大于或等于数组中的元素数,则行为未定义且几乎肯定是不希望的。 - 要求
-
T
是一个完整类型。
get
T* get() const noexcept;
-
- 返回
-
存储的指针。
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
是一个完整类型。
swap
void swap(shared_array<T>& b) noexcept;
-
- 效果
-
交换两个智能指针的内容。
自由函数
比较
template<class T> bool
operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
-
- 返回
-
比较两个智能指针的存储指针的结果。
注意
|
提供 operator< 运算符重载是为了定义顺序,以便 shared_array 对象可以用于关联容器,例如 std::map 。 实现使用 std::less<T*> 来执行比较。 这确保了比较得到正确的处理,因为标准指出指针上的关系运算是未指定的(5.9 [expr.rel] 第 2 段),但 std::less 在指针上是明确定义的(20.3.3 [lib.comparisons] 第 8 段)。 |
swap
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
-
- 返回
-
a.swap(b)
. - 要求
-
T
是一个完整类型。
附录 D:版权和许可
本文档是
-
版权所有 1999 Greg Colvin
-
版权所有 1999 Beman Dawes
-
版权所有 2002 Darin Adler
-
版权所有 2003-2020 Peter Dimov
-
版权所有 2005, 2006 Ion Gaztañaga
-
版权所有 2008 Frank Mori Hess
-
版权所有 2012-2017 Glen Fernandes
-
版权所有 2013 Andrey Semashev
并根据 Boost Software License, Version 1.0 许可分发。