Boost C++ 库

...世界上最受推崇和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

简介

智能指针是存储指向动态分配(堆)对象的指针的对象。 它们的行为很像内置 C++ 指针,除了它们会在适当的时候自动删除指向的对象。 智能指针在面对异常时特别有用,因为它们确保动态分配对象的正确销毁。 它们也可以用于跟踪由多个所有者共享的动态分配对象。

从概念上讲,智能指针被视为拥有指向的对象,因此负责在不再需要该对象时删除该对象。 因此,它们是 Bjarne Stroustrup 的 “C++ 编程语言” 第 3 版第 14.4 节资源管理中描述的 “资源获取即初始化” 惯用法的示例。

此库提供了六个智能指针类模板

  • scoped_ptr,用于将动态分配对象的所有权限制在当前作用域内;

  • scoped_array,为动态分配数组提供作用域所有权;

  • shared_ptr,用于管理对象或数组的共享所有权的通用工具;

  • weak_ptrshared_ptr 管理的对象的非拥有观察者,可以临时提升为 shared_ptr

  • intrusive_ptr,指向具有嵌入式引用计数的对象的指针;

  • local_shared_ptr,在单个线程内提供共享所有权。

shared_ptrweak_ptr 自 2011 年迭代以来一直是 C++ 标准的一部分。

此外,该库还包含以下支持实用程序函数和类

作为一般规则,库中指针管理的对象的析构函数或 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_HOOKSBOOST_SP_USE_STD_ALLOCATORBOOST_SP_USE_QUICK_ALLOCATORBOOST_AC_USE_SPINLOCKBOOST_AC_USE_PTHREADSBOOST_SP_USE_SPINLOCKBOOST_SP_USE_PTHREADS 启用的功能已被弃用,并且将在未来的版本中删除对其的支持。

1.79.0 版本变更

  • 添加了 get_allocator_pointer

1.74.0 版本变更

  • shared_ptrweak_ptrlocal_shared_ptr 添加了 owner_equals

  • shared_ptrweak_ptr 添加了 owner_hash_value

  • 添加了 owner_equal_toowner_hash

  • shared_ptrlocal_shared_ptr 添加了 std::hash 特化

  • weak_ptr 添加了 boost::hash 支持,以及 std::hashstd::equal_to 特化

1.72.0 版本变更

  • 添加了 allocate_unique

1.71.0 版本变更

  • weak_ptr 添加了别名构造函数

  • 添加了 weak_ptr<T>::empty()

  • 添加了 enable_shared_fromshared_fromweak_from

1.65.0 版本变更

  • 添加了 atomic_shared_ptr

  • 添加了 local_shared_ptrmake_local_shared

scoped_ptr:作用域对象所有权

描述

scoped_ptr 类模板存储指向动态分配对象的指针。(动态分配对象使用 C++ new 表达式分配。)指向的对象保证会被删除,无论是在 scoped_ptr 销毁时,还是通过显式的 reset 删除。 请参阅 示例

scoped_ptr 是满足简单需求的简单解决方案。 它提供了一个基本的 “资源获取即初始化” 工具,没有共享所有权或所有权转移语义。 它的名称和语义的强制执行(通过不可复制)都表明了其意图,即仅在当前作用域内保留所有权。 因为它是不可复制的,所以对于不应复制的指针,它比 shared_ptr 更安全。

由于 scoped_ptr 很简单,因此在其常用实现中,每个操作都与内置指针一样快,并且没有比内置指针更多的空间开销。

scoped_ptr 不能在 C++ 标准库容器中使用。 如果需要可以在其中使用的智能指针,请使用 shared_ptrstd::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_ptrstd::unique_ptr 的主要原因是让代码的读者知道您打算仅对当前作用域应用 “资源获取即初始化”,并且无意转移所有权。

使用 scoped_ptr 的第二个原因是防止以后的维护程序员添加通过返回 auto_ptr 来转移所有权的函数,因为维护程序员看到了 auto_ptr,并假设可以安全地转移所有权。

想想 boolint。 我们都知道,在底层,bool 通常只是一个 int。 实际上,有些人反对在 C++ 标准中包含 bool,因为那样做。 但是,通过编写 bool 而不是 int,您可以告诉读者您的意图是什么。 scoped_ptr 也是如此; 通过使用它,您是在发出意图信号。

有人建议 scoped_ptr<T> 等效于 std::auto_ptr<T> const。 然而,Ed Brey 指出,resetstd::auto_ptr<T> const 上不起作用。

句柄/主体 惯用法

scoped_ptr 的一个常见用法是实现句柄/主体(也称为 pimpl)惯用法,以避免在头文件中暴露主体(实现)。

scoped_ptr_example_test.cpp 示例程序包含一个头文件 scoped_ptr_example.hpp,该文件使用指向不完整类型的 scoped_ptr<> 来隐藏实现。 需要完整类型的成员函数的实例化发生在 scoped_ptr_example.cpp 实现文件中。

常见问题解答

  1. 为什么 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::vectorscoped_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 被销毁或重置时,指向的对象保证会被删除。

代码示例 1. 使用 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++ 标准库的 CopyConstructibleMoveConstructibleCopyAssignableMoveAssignable 要求,并且可以在标准库容器中使用。 提供了比较运算符,以便 shared_ptr 可以与标准库的关联容器一起使用。

由于实现使用引用计数,因此 shared_ptr 实例的循环将不会被回收。 例如,如果 main() 持有指向 Ashared_ptr,而 A 直接或间接地持有返回到 Ashared_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>,其中 UT 的可访问基类,以及转换为 shared_ptr<void>

shared_ptr 现在是 C++11 标准的一部分,即 std::shared_ptr

从 Boost 1.53 版本开始,shared_ptr 可以用于保存指向动态分配数组的指针。 这是通过使用数组类型(T[]T[N])作为模板参数来实现的。 使用无大小数组 T[] 和有大小数组 T[N] 之间几乎没有区别; 后者仅使 operator[] 能够对索引执行范围检查。

代码示例 2. 将 shared_ptr 与数组一起使用
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; 让 TY 成为相同类型,或者将参数传递给 Y 的构造函数也是可以的。

如果遵守此指南,自然而然地,您将没有显式的 delete 语句; try/catch 构造将很少见。

避免使用未命名的 shared_ptr 临时对象来节省打字; 要了解为什么这很危险,请考虑以下示例

代码示例 3. 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_sharedallocate_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_typeT,当 TU[]U[N] 时,element_typeU

默认构造函数

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 必须是良构的、良好定义的,并且不得抛出异常。 当 TU[N] 时,Y(*)[N] 必须可转换为 T*; 当 TU[] 时,Y(*)[] 必须可转换为 T*; 否则,Y* 必须可转换为 T*

    效果

    T 不是数组类型时,构造一个拥有指针 pshared_ptr。 否则,构造一个拥有 p 和未指定类型的析构器(调用 delete[] p)的 shared_ptr

    后置条件

    use_count() == 1 && get() == p。 如果 T 不是数组类型,并且 p 可以明确转换为某个 Venable_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 必须是 CopyConstructibleD 的复制构造函数和析构函数不得抛出异常。 表达式 d(p) 必须是良构的、良好定义的,并且不得抛出异常。 A 必须是 Allocator,如 C++ 标准的分配器要求 [allocator.requirements] 节中所述。 当 TU[N] 时,Y(*)[N] 必须可转换为 T*; 当 TU[] 时,Y(*)[] 必须可转换为 T*; 否则,Y* 必须可转换为 T*

    效果

    构造一个拥有指针 p 和析构器 dshared_ptr。 接受分配器 a 的构造函数使用 a 的副本分配内存。

    后置条件

    use_count() == 1 && get() == p。 如果 T 不是数组类型,并且 p 可以明确转换为某个 Venable_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() == puse_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<int> p(new int);
shared_ptr<void> q(p);
p = p;
q = p;

两个赋值都可能是空操作。

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。 如果 TU[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_castweak_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,当且仅当 *thisr 共享所有权或两者都为空时。

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::setshared_ptr 对象。

请注意,在容器填充后,某些 shared_ptr 对象的使用计数将为 1 而不是 2,因为该集合是 std::set 而不是 std::multiset,因此不包含重复条目。此外,在执行 push_backinsert 容器操作时,使用计数在不同时间可能会更高。更复杂的是,容器操作可能在各种情况下抛出异常。如果没有智能指针,在这个例子中正确处理内存管理和异常处理将是一场噩梦。

句柄/主体 惯用法

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));
代码示例 4. 从两个线程读取 shared_ptr
// thread A
shared_ptr<int> p2(p); // reads p

// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
代码示例 5. 从两个线程写入不同的 shared_ptr 实例
// thread A
p.reset(new int(1912)); // writes p

// thread B
p2.reset(); // OK, writes p2
代码示例 6. 从两个线程读取和写入 shared_ptr
// thread A
p = p3; // reads p3, writes p

// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
代码示例 7. 从两个线程读取和销毁 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"
代码示例 8. 从两个线程写入 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 的库,您可以在项目范围内 #defineBOOST_SP_DISABLE_THREADS 以切换到普通的非原子引用计数更新。

(在某些但并非所有翻译单元中定义 BOOST_SP_DISABLE_THREADS 在技术上违反了单一定义规则和未定义行为。然而,实现会尽力满足在这些翻译单元中使用非原子更新的请求。但是,不保证。)

您可以定义宏 BOOST_SP_USE_PTHREADS 以关闭无锁的平台特定实现,并回退到通用的基于 pthread_mutex_t 的代码。

常见问题解答

  1. 共享指针有几种变体,具有不同的权衡;为什么智能指针库只提供一个实现?能够尝试每种类型以找到最适合手头工作的类型是否会很有用?

    shared_ptr 的一个重要目标是提供标准的共享所有权指针。拥有单一的指针类型对于稳定的库接口非常重要,因为不同的共享指针通常无法互操作,即,引用计数指针(库 A 使用)无法与链接指针(库 B 使用)共享所有权。

  2. 为什么 shared_ptr 没有提供 traits 或 policies 的模板参数来允许广泛的用户自定义?

    参数化会阻止用户。 shared_ptr 模板经过精心设计,可以在没有广泛参数化的情况下满足常见需求。

  3. 我并不信服。默认参数可以在适当的地方使用以隐藏复杂性。再说一次,为什么不使用 policies 呢?

    模板参数会影响类型。请参阅上面第一个问题的答案。

  4. 为什么 shared_ptr 不使用链表实现?

    链表实现没有提供足够的优势来抵消额外指针的增加成本。此外,使链表实现线程安全成本很高。

  5. 为什么 shared_ptr(或任何其他 Boost 智能指针)不提供到 T* 的自动转换?

    自动转换被认为太容易出错。

  6. 为什么 shared_ptr 提供 use_count()

    作为编写测试用例和调试显示的辅助手段。其中一个始祖具有 use_count(),并且它在跟踪一个复杂项目中被证明有循环依赖关系的错误时非常有用。

  7. 为什么 shared_ptr 不指定复杂度要求?

    因为复杂度要求限制了实现者并使规范复杂化,而对 shared_ptr 用户没有明显的好处。例如,如果错误检查实现必须满足严格的复杂度要求,则它们可能会变得不符合规范。

  8. 为什么 shared_ptr 不提供 release() 函数?

    除非 shared_ptrunique() 的,否则它不能放弃所有权,因为另一个副本仍然会销毁该对象。

    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 可能是使用自定义删除器创建的,或者可能指向不同类型的对象。

  9. 为什么 operator->() 是 const,但其返回值是指向元素类型的非 const 指针?

    浅拷贝指针(包括原始指针)通常不传播常量性。这样做意义不大,因为您始终可以从 const 指针获得非 const 指针,然后通过它修改对象。 shared_ptr “尽可能接近原始指针,但不再接近”。

weak_ptr:非拥有观察者

描述

weak_ptr 类模板存储对已由 shared_ptr 管理的对象的“弱引用”。要访问对象,可以使用接受 weak_ptrshared_ptr 构造函数,或 weak_ptr 成员函数 lockweak_ptr 转换为 shared_ptr。当指向对象的最后一个 shared_ptr 消失且对象被删除时,尝试从引用已删除对象的 weak_ptr 实例获取 shared_ptr 将会失败:构造函数将抛出 boost::bad_weak_ptr 类型的异常,并且 weak_ptr::lock 将返回一个空的 shared_ptr

每个 weak_ptr 都满足 C++ 标准库的 CopyConstructibleAssignable 要求,因此可以在标准库容器中使用。提供了比较运算符,以便 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_typeT,当 TU[]U[N] 时,element_typeU

构造函数

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;
  • 效果

    构造一个持有值 rweak_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,当且仅当 *thisr 共享所有权或两者都为空时。

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)

常见问题解答

  1. 对象可以在其构造函数中创建指向自身的 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_sharedallocate_shared 提供了创建 shared_ptr 对象的便捷、安全和高效的方式。

理由

一致地使用 shared_ptr 可以消除使用显式 delete 的需要,但它本身并不能避免显式的 new。用户反复要求提供一个工厂函数,该函数创建一个给定类型的对象并返回指向它的 shared_ptr。除了方便和风格之外,这样的函数也是异常安全的,并且速度快得多,因为它可以使用单个分配来分配对象及其相应的控制块,从而消除了 shared_ptr 构造开销的很大一部分。这消除了关于 shared_ptr 的主要效率抱怨之一。

提供的重载函数模板系列 make_sharedallocate_shared 就是为了解决此需求。 make_shared 使用全局 operator new 来分配内存,而 allocate_shared 使用用户提供的分配器,从而允许更精细的控制。

选择名称 make_shared 的理由是,表达式 make_shared<Widget>() 可以大声朗读并传达预期的含义。

最初,Boost 函数模板 allocate_sharedmake_shared 仅针对标量对象提供。需要有效地分配数组对象。对类模板 shared_array 的一个批评始终是缺少像 make_shared 这样的仅使用单个分配的实用程序。当 shared_ptr 增强以支持数组类型时,为数组类型提供了 allocate_sharedmake_shared 的附加重载。

概要

make_sharedallocate_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_sharedallocate_shared 重载的通用要求如下所述。

要求

A 应该是分配器A 的复制构造函数和析构函数不应抛出异常。

效果

为类型 T 的对象或类型 Un 个对象(如果 TU[] 形式的数组类型,并且 n 由参数确定,如具体重载所指定)分配内存。对象从参数初始化,如具体重载所指定。使用 a 的重新绑定的副本(对于未指定的 value_type)来分配内存。如果抛出异常,则这些函数无效。

返回

一个 shared_ptr 实例,用于存储和拥有新构造对象的地址。

后置条件

r.get() != 0r.use_count() == 1,其中 r 是返回值。

抛出

std::bad_alloc,从 A::allocate 或从对象的初始化抛出的异常。

Remarks
  • 执行不超过一次内存分配。这提供了与侵入式智能指针相当的效率。

  • 当指定将数组类型的对象初始化为相同类型 v 的值时,应将其解释为对象的每个数组元素都初始化为来自 v 的相应元素。

  • 当指定将数组类型的对象进行值初始化时,应将其解释为对象的每个数组元素都进行值初始化。

  • 当指定将非数组类型 U 的(子)对象初始化为值 v,或从 args... 构造时,make_shared 应通过表达式 ::new(p) U(expr)(其中 expr 分别是 vstd::forward<Args>(args)...))执行此初始化,并且 p 的类型为 void*,并指向适合容纳类型 U 的对象的存储空间。

  • 当指定将非数组类型 U 的(子)对象初始化为值 v,或从 args... 构造时,allocate_shared 应通过表达式 std::allocator_traits<A2>::construct(a2, p, expr)(其中 expr 分别是 vstd::forward<Args>(args)...))执行此初始化,p 指向适合容纳类型 U 的对象的存储空间,并且类型为 A2a2a 的潜在重新绑定的副本。

  • 当指定将非数组类型 U 的(子)对象进行默认初始化时,make_shared_noinitallocate_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 的对象的存储空间,并且类型为 A2a2a 的潜在重新绑定的副本。

  • 数组元素按其地址的升序初始化。

  • 当由返回值管理的对象生命周期结束时,或者当数组元素的初始化抛出异常时,应以与其构造顺序相反的顺序销毁已初始化的元素。

注意
这些函数通常会分配比元素对象的总大小更多的内存,以便容纳内部簿记结构,例如引用计数。

自由函数

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_ptrweak_ptr

enable_shared_from_this<T> 定义了两个名为 shared_from_this 的成员函数,它们根据常量性返回指向 thisshared_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
注意

weak_this_ 由指向 *this 的指针构造时,shared_ptr 会将其初始化为自身的副本。例如,在以下代码中

class Y: public boost::enable_shared_from_this<Y> {};

int main()
{
    boost::shared_ptr<Y> p(new Y);
}

p 的构造将自动将 p->weak_this_ 初始化为 p

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_fromweak_from,在给定指向对象的原始指针的情况下获取 shared_ptrweak_ptr

enable_shared_fromenable_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_uniqueallocate_unique_noinit 重载的通用要求如下所述。

要求

A 应该是分配器A 的复制构造函数和析构函数不应抛出异常。

效果

为类型 T 的对象或类型 Un 个对象(如果 TU[] 形式的数组类型,并且 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 分别是 vstd::forward<Args>(args)...))执行此初始化,p 指向适合容纳类型 U 的对象的存储空间,并且类型为 A2a2a 的潜在重新绑定的副本。

  • 当指定将非数组类型 U 的(子)对象进行默认初始化时,allocate_unique_noinit 应通过表达式 ::new(p) U 执行此初始化,其中 p 的类型为 void*,并指向适合容纳类型 U 的对象的存储空间。

  • 当指定将非数组类型 U 的(子)对象进行值初始化时,allocate_unique 应通过表达式 std::allocator_traits<A2>::construct(a2, p) 执行此初始化,其中 p 指向适合容纳类型 U 的对象的存储空间,并且类型为 A2a2a 的潜在重新绑定的副本。

  • 数组元素按其地址的升序初始化。

  • 当由返回值管理的对象生命周期结束时,或者当数组元素的初始化抛出异常时,应以与其构造顺序相反的顺序销毁已初始化的元素。

自由函数

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_deleterallocate_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_refintrusive_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_refintrusive_ptr_release 函数,它们根据需要修改引用计数器,并在计数器降至零时销毁用户对象。

类模板在 DerivedCounterPolicy 参数上参数化。第一个参数是用户从 intrusive_ref_counter 派生的类。需要此类型以便在没有剩余引用时正确销毁对象。

第二个参数是一个策略,用于定义引用计数器的性质。库提供了两个这样的策略:thread_unsafe_counterthread_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 创建,引用同一个对象,但不共享同一个计数,因此可以安全地被两个不同的线程使用。

代码示例 9. 从 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( p1 ); // p3.local_use_count() also 1

但是,从第一个 local_shared_ptr 创建第二个 local_shared_ptr 会导致两者共享同一个计数

代码示例 10. 从另一个 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 实例确实共享所有权

代码示例 11. 从 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_typeT,当 TU[]U[N] 时,element_typeU

默认构造函数

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 );
  • 效果

    构造一个拥有 rlocal_shared_ptr

    后置条件

    local_use_count() == 1get() 返回 r.get() 的旧值。

    抛出

    std::bad_alloc,或当无法获得内存以外的资源时,实现定义的异常。

别名构造函数

template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
  • 效果

    构造一个与 r 共享所有权并存储 plocal_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() == plocal_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。 如果 TU[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,当且仅当 *thisr 共享所有权或两者都为空时。

自由函数

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_sharedallocate_local_shared 提供了创建 local_shared_ptr 对象的便捷、安全和高效的方式。它们类似于 shared_ptrmake_sharedallocate_shared

概要

make_local_sharedallocate_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_sharedallocate_shared 相同,只是返回的是 local_shared_ptr

通用指针转换

描述

指针转换函数模板(static_pointer_castdynamic_pointer_castconst_pointer_castreinterpret_pointer_cast)为原始指针、std::shared_ptrstd::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_castdynamic_pointer_castconst_pointer_castreinterpret_pointer_cast 对于原始指针、std::shared_ptrstd::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::hashstd::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);
}

这里需要注意的关键是,编译器生成的复制构造函数、赋值运算符和析构函数都具有合理的含义。因此,fileCopyConstructibleAssignable,允许其在标准容器中使用。

使用抽象类进行实现隐藏

另一种广泛使用的 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_refintrusive_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 内部,我们想创建一个指向 *pshared_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 时,这引出了构造函数如何获取指向 thisshared_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 成员。构造指向 implshared_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_ptrweak_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_ptrcounted_ptr 的类,它们与我们现在称为 scoped_ptrshared_ptr 的类非常相似。在少数几个图书馆工作组的建议未被全体委员会采纳的情况下,counted_ptr 被拒绝,令人惊讶的所有权转移语义被添加到 auto_ptr

1998 年 10 月

Beman Dawes 提议以 safe_ptrcounted_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::swapshared_array::swap

1999 年 11 月

Darin Adler 为共享类型提供了 operator ==operator != 以及 std::swapstd::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_ptrweak_ptr 包含在标准库中,通过第一个库技术报告(称为 TR1)。该提案被接受,并最终成为 2011 年迭代的 C++ 标准的一部分。

2007 年 7 月

Peter Dimov 和 Beman Dawes 提议shared_ptr 进行一些增强,因为它正在进入最终成为 C++11 标准的工作文件。

2012 年 11 月

Glen Fernandes 提供了用于数组的 make_sharedallocate_shared 的实现。它们为可以使用构造函数参数或初始化列表初始化的数组以及默认初始化和无值初始化的重载实现了单次分配。

Peter Dimov 通过扩展 shared_ptr 以通过语法 shared_ptr<T[]>shared_ptr<T[N]> 支持数组,从而帮助了这项开发。

2013 年 4 月

Peter Dimov 提议shared_ptr 的扩展以支持数组包含在标准中,并且该提议被接受。

2014 年 2 月

Glen Fernandes 更新了 make_sharedallocate_shared 以符合 C++ 标准论文 N3870 中的规范,并为数组和对象实现了 make_unique

Peter Dimov 和 Glen Fernandes 分别更新了标量和数组实现,以解决 C++ 标准库缺陷 2070。

2017 年 2 月

Glen Fernandes 为数组重写了 allocate_sharedmake_shared,以获得更优化和更易于维护的实现。

2017 年 6 月

Peter Dimov 和 Glen Fernandes 以 Asciidoc 格式重写了文档。

Peter Dimov 添加了 atomic_shared_ptrlocal_shared_ptr

2019 年 8 月

Glen Fernandes 为标量和数组实现了 allocate_unique

附录 C:shared_array(已弃用)

注意
此功能已弃用,因为现在可以使用 shared_ptrT[]T[N],并且在各方面都更优越。

描述

shared_array 类模板存储指向动态分配数组的指针。(动态分配的数组使用 C++ new[] 表达式分配。)当指向它的最后一个 shared_array 被销毁或重置时,保证指向的对象被删除。

每个 shared_array 都满足 C++ 标准库的 CopyConstructibleAssignable 要求,因此可以在标准库容器中使用。提供了比较运算符,以便 shared_array 可以与标准库的关联容器一起使用。

通常,shared_array 不能正确地保存指向使用非数组形式的 new 分配的对象的指针。有关该用法,请参阅 shared_ptr

由于实现使用引用计数,因此 shared_array 实例的循环将不会被回收。例如,如果 main 持有指向 A 的 shared_array,而 A 直接或间接地持有返回 A 的 shared_array,则 A 的使用计数将为 2。销毁原始 shared_array 将使 A 悬空,使用计数为 1。

指向 std::vectorshared_ptrshared_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,存储 pd 的副本。之后,使用计数为 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 是一个完整类型。

本文档是

  • 版权所有 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 许可分发。