Boost C++ 库

……世界上最受推崇和设计精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码规范

引言

智能指针是存储指向动态分配(堆)对象的指针的对象。它们的行为与内置 C++ 指针非常相似,不同之处在于它们会在适当的时间自动删除指向的对象。智能指针在出现异常时特别有用,因为它们确保正确销毁动态分配的对象。它们还可以用于跟踪由多个所有者共享的动态分配的对象。

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

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

  • scoped_ptr,用于将动态分配对象的拥有权包含到当前作用域;

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

  • shared_ptr,一个用于管理对象或数组的共享所有权的多功能工具;

  • weak_ptr,一个指向由 shared_ptr 管理的对象的非拥有者观察者,可以临时提升为 shared_ptr;

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

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

shared_ptrweak_ptr 自 2011 年起就是 C++ 标准的一部分。

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

  • make_sharedallocate_shared,用于创建返回 shared_ptr 的对象的工厂函数;

  • make_unique,一个返回 std::unique_ptr 的工厂函数;

  • allocate_unique,一个使用分配器创建对象的工厂函数,返回 std::unique_ptr

  • enable_shared_from_this,一个辅助基类,允许获取指向 this 的 shared_ptr;

  • enable_shared_fromenable_shared_from_this 的更新和更好的替代方案;

  • pointer_to_other,一个用于将一种智能指针类型转换为另一种类型的辅助特性;

  • static_pointer_cast 及其配套项,泛型智能指针转换;

  • intrusive_ref_counter,一个包含引用计数的辅助基类。

  • atomic_shared_ptr,一个辅助类,为类型为 shared_ptr 的值实现 std::atomic 的接口。

一般来说,库中指针管理的对象的析构函数或 operator delete 不允许抛出异常。

修订历史

1.87.0 中的更改

  • C++03 已不再支持,需要 C++11 编译器。这包括 GCC 4.8 或更高版本,以及 MSVC 14.0 或更高版本。

  • BOOST_SP_ENABLE_DEBUG_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::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 必须不抛出异常,并且在实例化 scoped_ptr::~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 指出,reset方法无法作用于std::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_ptrrelease()成员,则可以转移被持有指针的所有权,削弱其将资源生命周期限制在给定上下文中的作用。在需要转移所有权的情况下,使用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,即指向的对象的类型。

概要

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的销毁将留下使用计数为1的悬空A。使用weak_ptr来“打破循环”。

类模板的参数化对象为T,即指向的对象的类型。shared_ptr及其大多数成员函数对T没有要求;它可以是不完整类型或void。确实会施加附加要求的成员函数(构造函数、reset)在下面有明确的说明。

只要T*可以隐式转换为U*shared_ptr<T>就可以隐式转换为shared_ptr<U>。特别是,shared_ptr<T>可以隐式转换为shared_ptr<T const>,转换为shared_ptr<U>(其中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_ptrTY是相同的类型,或者将参数传递给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和一个未指定类型的析构器的shared_ptr,该析构器调用delete[] p

    后置条件

    use_count() == 1 && get() == p。如果T不是数组类型并且p可以明确转换为某个Venable_shared_from_this<V>*,则p->shared_from_this()返回*this的副本。

    抛出

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

    异常安全性

    如果抛出异常,构造函数将调用delete[] p(当T是数组类型时),或者调用delete p(当T不是数组类型时)。

注意
p必须是指向通过C++ new表达式分配的对象的指针,或者为0。即使p为0,使用计数为1的后置条件也成立;对值为0的指针调用delete是无害的。
注意
此构造函数是一个模板,以便记住传递的实际指针类型。即使T没有虚析构函数或为void,析构函数也将使用相同的指针调用delete,包括其原始类型。

带有析构器的构造函数

template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
  • 要求

    D必须是CopyConstructibleD的复制构造函数和析构函数不能抛出异常。表达式d(p)必须是良构的、定义良好的,并且不能抛出异常。A必须是分配器,如C++标准的[allocator.requirements]部分中所述。当TU[N]时,Y(*)[N]必须可转换为T*;当TU[]时,Y(*)[]必须可转换为T*;否则,Y*必须可转换为T*

    效果

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

    后置条件

    use_count() == 1 && get() == p。如果T不是数组类型并且p可以明确转换为某个Venable_shared_from_this<V>*,则p->shared_from_this()返回*this的副本。

    抛出

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

    异常安全性

    如果抛出异常,则调用d(p)

注意
当需要删除p指向的对象时,将使用存储的d的副本以及存储的p的副本作为参数来调用它。
注意
自定义析构器允许返回shared_ptr的工厂函数将用户与内存分配策略隔离开来。由于析构器不是类型的一部分,因此更改分配策略不会破坏源代码或二进制兼容性,也不需要客户端重新编译。例如,“无操作”析构器在返回指向静态分配对象的shared_ptr时很有用,其他变体允许shared_ptr用作另一个智能指针的包装器,从而简化互操作性。
注意
D的复制构造函数不抛出异常的要求源于值传递。如果复制构造函数抛出异常,则指针将会泄漏。

复制构造函数和转换构造函数

shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
  • 要求

    Y*应该可以转换为T*

    效果

    如果r为空,则构造一个空的shared_ptr;否则,构造一个与r共享所有权的shared_ptr

    后置条件

    get() == r.get() && use_count() == r.use_count().

移动构造函数

shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
  • 要求

    Y*应该可以转换为T*

    效果

    r移动构造一个shared_ptr

    后置条件

    *this包含r的旧值。r为空且r.get() == 0

别名构造函数

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

    r复制构造一个shared_ptr,同时存储p

    后置条件

    get() == p && use_count() == r.use_count().

别名移动构造函数

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

    r移动构造一个shared_ptr,同时存储p

    后置条件

    get() == puse_count()等于r的旧计数。r为空且r.get() == 0

weak_ptr构造函数

template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
  • 要求

    Y*应该可以转换为T*

    效果

    构造一个与r共享所有权的shared_ptr,并存储r中存储的指针的副本。

    后置条件

    use_count() == r.use_count().

    抛出

    r.use_count() == 0时,抛出bad_weak_ptr异常。

    异常安全性

    如果抛出异常,则构造函数无效。

auto_ptr构造函数

template<class Y> shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
  • 要求

    Y*应该可以转换为T*

    效果

    构造一个shared_ptr,如同存储r.release()的副本一样。

    后置条件

    use_count() == 1.

    抛出

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

    异常安全性

    如果抛出异常,则构造函数无效。

unique_ptr构造函数

template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
  • 要求

    Y*应该可以转换为T*

    效果
    • r.get() == 0时,等效于shared_ptr()

    • D不是引用类型时,等效于shared_ptr(r.release(), r.get_deleter())

    • 否则,等效于shared_ptr(r.release(), del),其中del是一个删除器,它存储从r.get_deleter()返回的引用rd,并且del(p)调用rd(p)

    抛出

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

    异常安全性

    如果抛出异常,则构造函数无效。

析构函数

~shared_ptr() noexcept;
  • 效果
    • 如果*this为空,或者与另一个shared_ptr实例共享所有权(use_count() > 1),则没有副作用。

    • 否则,如果*this拥有指针p和删除器d,则调用d(p)

    • 否则,*this拥有指针p,则调用delete p

赋值

shared_ptr & operator=(shared_ptr const & r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
  • 效果

    等效于shared_ptr(r).swap(*this)

    返回值

    *this.

注意
由临时对象构造和析构引起的引用计数更新不被认为是可观察的副作用,实现可以自由地通过不同的方式满足效果(以及隐含的保证),而无需创建临时对象。
注意

特别是,在以下示例中

shared_ptr<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;
  • 返回值

    存储的指针。

唯一性

bool unique() const noexcept;
  • 返回值

    use_count() == 1.

use_count

long use_count() const noexcept;
  • 返回值

    包括*this在内,共享*this所有权的shared_ptr对象的个数,或者当*this为空时为0。

转换

explicit operator bool() const noexcept;
  • 返回值

    get() != 0.

    注意
    此转换运算符允许在布尔上下文中使用shared_ptr对象,例如if(p && p->valid()) {}
注意
转换为bool不仅仅是语法糖。它允许在使用dynamic_pointer_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;
  • 返回值

    当且仅当*thisr共享所有权或都为空时为true

owner_hash_value

std::size_t owner_hash_value() const noexcept;
  • 返回值

    一个未指定的哈希值,使得共享所有权的两个实例具有相同的哈希值。

自由函数

比较

template<class T, class U>
  bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
  • 返回值

    a.get() == b.get().

template<class T, class U>
  bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
  • 返回值

    a.get() != b.get().

template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
  • 返回值

    p.get() == 0.

template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
  • 返回值

    p.get() != 0.

template<class T, class U>
  bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
  • 返回值

    一个未指定的值,使得

    • operator<是C++标准[lib.alg.sorting]节中描述的严格弱排序;

    • 在由operator<定义的等价关系下,!(a < b) && !(b < a),两个shared_ptr实例等价当且仅当它们共享所有权或都为空。

注意
允许将shared_ptr对象用作关联容器中的键。
注意
出于设计原因,省略了其余的比较运算符。

swap

template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
  • 效果

    相当于 a.swap(b)

get_pointer

template<class T>
  typename shared_ptr<T>::element_type *
    get_pointer(shared_ptr<T> const & p) noexcept;
  • 返回值

    p.get().

    注意
    作为泛型编程的辅助工具提供。被mem_fn使用。

static_pointer_cast

template<class T, class U>
  shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
  • 要求

    表达式static_cast( (U*)0 )必须是良构的。

    返回值

    shared_ptr( r, static_cast::element_type*>(r.get()) ).

警告
看似等效的表达式shared_ptr(static_cast(r.get()))最终会导致未定义的行为,试图删除同一个对象两次。

const_pointer_cast

template<class T, class U>
  shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
  • 要求

    表达式const_cast( (U*)0 )必须是良构的。

    返回值

    shared_ptr( r, const_cast::element_type*>(r.get()) ).

dynamic_pointer_cast

template<class T, class U>
    shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
  • 要求

    表达式dynamic_cast( (U*)0 )必须是良构的。

    返回值
    • dynamic_cast::element_type*>(r.get())返回非零值p时,shared_ptr(r, p)

    • 否则,shared_ptr()

reinterpret_pointer_cast

template<class T, class U>
  shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
  • 要求

    表达式reinterpret_cast( (U*)0 )必须是良构的。

    返回值

    shared_ptr( r, reinterpret_cast::element_type*>(r.get()) ).

operator<<

template<class E, class T, class Y>
  std::basic_ostream<E, T> &
    operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
  • 效果

    os << p.get();.

    返回值

    os.

get_deleter

template<class D, class T>
  D * get_deleter(shared_ptr<T> const & p) noexcept;
  • 返回值

    如果*this拥有类型为(cv-unqualified)D的删除器d,则返回&d;否则返回0。

原子访问

注意
本节中的函数对于由*p标识的第一个shared_ptr参数是原子的。如果仅通过本节中的函数进行操作,则对同一shared_ptr实例的并发访问不是数据竞争。
template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;
  • 返回值

    false.

    注意
    此实现不是无锁的。
template<class T> shared_ptr<T> atomic_load( shared_ptr<T> const * p ) noexcept;
template<class T> shared_ptr<T> atomic_load_explicit( shared_ptr<T> const * p, int ) noexcept;
  • 返回值

    *p.

    注意
    int参数是memory_order,但是此实现不使用它,因为它基于锁,因此始终是顺序一致的。
template<class T>
  void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
  void atomic_store_explicit( shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
  • 效果

    p->swap(r).

template<class T>
  shared_ptr<T> atomic_exchange( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
  shared_ptr<T> atomic_exchange_explicit(
    shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
  • 效果

    p->swap(r).

    返回值

    *p的旧值。

template<class T>
  bool atomic_compare_exchange(
    shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w ) noexcept;
template<class T>
  bool atomic_compare_exchange_explicit(
    shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w, int, int ) noexcept;
  • 效果

    如果*p等效于*v,则将w赋值给*p,否则将*p赋值给*v

    返回值

    如果*p等效于*v则为true,否则为false

    备注

    如果两个shared_ptr实例存储相同的指针值并共享所有权,则它们是等效的。

示例

有关完整的示例程序,请参见shared_ptr_example.cpp。该程序构建了一个std::vector和一个std::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对象提供与内置类型相同的线程安全级别。多个线程可以同时“读取”(仅使用const操作访问)一个shared_ptr实例。多个线程可以同时“写入”(使用可变操作访问,例如operator=reset)不同的shared_ptr实例(即使这些实例是副本,并在底层共享相同的引用计数)。

任何其他同时访问都会导致未定义的行为。

示例

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没有模板参数来提供特性或策略以允许广泛的用户自定义?

    参数化会劝退用户。shared_ptr模板经过精心设计,可在无需广泛参数化的情况下满足常见需求。

  3. 我不信服。在适当的情况下可以使用默认参数来隐藏复杂性。再说一次,为什么不用策略呢?

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

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

    链表实现带来的好处不足以抵消额外指针带来的成本。此外,使链表实现线程安全代价昂贵。

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

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

  6. 为什么shared_ptr提供use_count()

    作为编写测试用例和调试显示的辅助手段。其中一个原型包含use_count(),它在追踪一个复杂的项目中的bug时非常有用,该项目最终存在循环依赖关系。

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

    因为复杂度要求会限制实现者,并在没有明显益处的情况下使规范复杂化。例如,如果必须满足严格的复杂度要求,错误检查实现可能会变得不符合规范。

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

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

    考虑一下

    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指针获得非常量指针,然后通过它修改对象。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,即指向的对象的类型。

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为空。

别名构造函数

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

r构造一个weak_ptr,就像使用相应的转换/复制/移动构造函数一样,但是存储p

后置条件

use_count() == r.use_count()。当!expired()时,shared_ptr(*this).get() == p

注意
这些构造函数是扩展,在std::weak_ptr中不存在。

析构函数

~weak_ptr() noexcept;
  • 效果

    销毁此weak_ptr,但对它存储的指针指向的对象没有影响。

赋值

weak_ptr & operator=(weak_ptr const & r) noexcept;
weak_ptr & operator=(weak_ptr && r) noexcept;
template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r) noexcept;
template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r) noexcept;
  • 效果

    相当于weak_ptr(r).swap(*this)

    注意
    实现可以自由地通过不同的方式满足效果(和隐含的保证),而无需创建临时对象。

use_count

long use_count() const noexcept;
  • 返回值

    如果*this为空,则为0;否则,与*this共享所有权的shared_ptr对象的个数。

expired

bool expired() const noexcept;
  • 返回值

    use_count() == 0.

empty

bool empty() const noexcept;
  • 返回值

    *this为空时为true,否则为false

    注意
    此函数是扩展,在std::weak_ptr中不存在。

lock

shared_ptr<T> lock() const noexcept;
  • 返回值

    expired()? shared_ptr(): shared_ptr(*this).

reset

void reset() noexcept;
  • 效果

    相当于weak_ptr().swap(*this)

swap

void swap(weak_ptr & b) noexcept;
  • 效果

    交换两个智能指针的内容。

owner_before

template<class Y> bool owner_before( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_before( shared_ptr<Y> const & r ) const noexcept;
  • 返回值

    参见operator<的描述。

owner_equals

template<class Y> bool owner_equals( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( shared_ptr<Y> const & r ) const noexcept;
  • 返回值

    当且仅当*thisr共享所有权或都为空时为true

owner_hash_value

std::size_t owner_hash_value() const noexcept;
  • 返回值

    一个未指定的哈希值,使得共享所有权的两个实例具有相同的哈希值。

自由函数

比较

template<class T, class U>
  bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
  • 返回值

    一个未指定的值,使得

    • operator<是C++标准[lib.alg.sorting]节中描述的严格弱排序;

    • 根据由operator<定义的等价关系!(a < b) && !(b < a),当且仅当两个weak_ptr实例共享所有权或都为空时,它们才等价。

注意
允许weak_ptr对象用作关联容器中的键。

swap

template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
  • 效果

    相当于 a.swap(b)

常见问题

  1. 对象可以在其构造函数中创建对自身的弱指针吗?

    不可以。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_sharedmake_shared使用全局operator new分配内存,而allocate_shared使用用户提供的分配器,允许更精细的控制。

选择名称make_shared的原因是表达式make_shared()可以朗读出来,并传达预期的含义。

最初,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的一个对象或nU对象(如果TU[]形式的数组类型,并且n由参数确定,由具体的重载指定)分配内存。对象根据具体的重载指定的参数进行初始化。使用a的重新绑定副本(对于未指定的value_type)来分配内存。如果抛出异常,则函数无效。

返回值

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

后置条件

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

抛出

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

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

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

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

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

  • 当指定将非数组类型U的(子)对象初始化为值v或从args...构造时,allocate_shared应通过表达式std::allocator_traits::construct(a2, p, expr)执行此初始化(其中_expr_分别是vstd::forward(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::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);
  • 约束条件

    T不是数组。

    返回值

    args...构造的指向类型T的对象的shared_ptr

    示例
  • auto p = make_shared();

  • auto p = make_shared<std::vector<int> >(16, 1);

template<class T>
  shared_ptr<T> make_shared(std::size_t n);
template<class T, class A>
  shared_ptr<T> allocate_shared(const A& a, std::size_t n);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>n 个值初始化对象的序列的 shared_ptr

    示例
  • auto p = make_shared<double[]>(1024);

  • auto p = make_shared<double[][2][2]>(6);

template<class T>
  shared_ptr<T> make_shared();
template<class T, class A>
  shared_ptr<T> allocate_shared(const A& a);
  • 约束条件

    T 是一个边界已知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>extent_v<T> 个值初始化对象的序列的 shared_ptr

    示例
  • auto p = make_shared<double[1024]>();

  • auto p = make_shared<double[6][2][2]>();

template<class T> shared_ptr<T>
  make_shared(std::size_t n, const remove_extent_t<T>& v);
template<class T, class A> shared_ptr<T>
  allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>n 个对象的序列的 shared_ptr,每个对象都初始化为 v

    示例
  • auto p = make_shared<double[]>(1024, 1.0);

  • auto p = make_shared<double[][2]>(6, {1.0, 0.0});

  • auto p = make_shared<std::vector<int>[]>(4, {1, 2});

template<class T>
  shared_ptr<T> make_shared(const remove_extent_t<T>& v);
template<class T, class A>
  shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);
  • 约束条件

    T 是一个边界已知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>extent_v<T> 个对象的序列的 shared_ptr,每个对象都初始化为 v

    示例
  • auto p = make_shared<double[1024]>(1.0);

  • auto p = make_shared<double[6][2]>({1.0, 0.0});

  • auto p = make_shared<std::vector<int>[4]>({1, 2});

template<class T>
  shared_ptr<T> make_shared_noinit();
template<class T, class A>
  shared_ptr<T> allocate_shared_noinit(const A& a);
  • 约束条件

    T 不是数组,或者是一个边界已知的数组。

    返回值

    一个指向类型为 T 的默认初始化对象的 shared_ptr,或者分别指向类型为 remove_extent_t<T>extent_v<T> 个默认初始化对象的序列的 shared_ptr

    示例

    auto p = make_shared_noinit<double[1024]>();

template<class T>
  shared_ptr<T> make_shared_noinit(std::size_t n);
template<class T, class A>
  shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>*n* 个默认初始化对象的序列的 shared_ptr

    示例

    auto p = make_shared_noinit<double[]>(1024);

enable_shared_from_this

描述

类模板 enable_shared_from_this 用作基类,允许从成员函数中获取指向当前对象的 shared_ptrweak_ptr

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

当它由指向 *this 的指针构造时,weak_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() 为 true 但仍然与其他引用该对象的 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() 为 true 但仍然与其他引用该对象的 weak_ptr 实例(如果有)共享所有权的 weak_ptr

make_unique:创建 unique_ptr

描述

make_unique 函数模板提供了创建 std::unique_ptr 对象的便捷且安全的方法。

基本原理

C++11 标准引入了 std::unique_ptr,但没有提供任何类似于 std::make_sharedmake_unique 实用程序,后者提供了相同的异常安全性和避免编写 new 表达式的便利性。在一些标准库厂商实现它之前(以及在 C++14 标准引入 std::make_unique 之前),由于用户的请求,本库提供了它。

本库还为默认初始化提供了 make_unique 的额外重载,当用户不需要或不想承担值初始化的开销时。C++20 标准现在通过 std::make_unique_for_overwrite 提供了此功能。

概要

make_unique 定义在 <boost/smart_ptr/make_unique.hpp> 中。

namespace boost {
  // T is not an array
  template<class T, class... Args>
    std::unique_ptr<T> make_unique(Args&&... args);

  // T is not an array
  template<class T>
    std::unique_ptr<T> make_unique(type_identity_t<T>&& v);

  // T is an array of unknown bounds
  template<class T>
    std::unique_ptr<T> make_unique(std::size_t n);

  // T is not an array
  template<class T>
    std::unique_ptr<T> make_unique_noinit();

  // T is an array of unknown bounds
  template<class T>
    std::unique_ptr<T> make_unique_noinit(std::size_t n);
}

自由函数

template<class T, class... Args>
  std::unique_ptr<T> make_unique(Args&&... args);
  • 约束条件

    T不是数组。

    返回值

    std::unique_ptr<T>(new T(std::forward<Args>(args)...).

    示例

    auto p = make_unique<int>();

template<class T>
  std::unique_ptr<T> make_unique(type_identity_t<T>&& v);
  • 约束条件

    T不是数组。

    返回值

    std::unique_ptr<T>(new T(std::move(v)).

    示例

    auto p = make_unique<std::vector<int> >({1, 2});

template<class T>
  std::unique_ptr<T> make_unique(std::size_t n);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    std::unique_ptr<T>(new remove_extent_t<T>[n]()).

    示例

    auto p = make_unique<double[]>(1024);

template<class T>
  std::unique_ptr<T> make_unique_noinit();
  • 约束条件

    T不是数组。

    返回值

    std::unique_ptr<T>(new T).

    示例

    auto p = make_unique_noinit<std::array<double, 1024> >();

template<class T>
  std::unique_ptr<T> make_unique_noinit(std::size_t n);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    std::unique_ptr<T>(new remove_extent_t<T>[n]).

    示例

    auto p = make_unique_noinit<double[]>(1024);

allocate_unique:创建 unique_ptr

描述

allocate_unique 函数模板系列提供了使用分配器创建管理新对象的 std::unique_ptr 的便捷且安全的方法。

基本原理

C++14 标准引入了使用 operator new 创建新对象的 std::make_unique。但是,标准库中没有方便的工具可以使用分配器来创建 std::unique_ptr 管理的对象。编写分配器感知代码的用户经常请求 allocate_unique 工厂函数。此函数与 std::unique_ptr 的关系如同 std::allocate_sharedstd::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的一个对象或nU对象(如果TU[]形式的数组类型,并且n由参数确定,由具体的重载指定)分配内存。对象根据具体的重载指定的参数进行初始化。使用a的重新绑定副本(对于未指定的value_type)来分配内存。如果抛出异常,则函数无效。

返回值

一个存储并拥有新构造对象地址的 std::unique_ptr 实例。

后置条件

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

抛出

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

备注
  • 当指定将数组类型的对象初始化为相同类型v的值时,这应解释为将对象的每个数组元素初始化为v中的相应元素。

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

  • 当指定非数组类型 U 的(子)对象初始化为值 v 或从 args... 构造时,allocate_unique 将通过表达式 std::allocator_traits<A2>::construct(a2, p, expr) 执行此初始化(其中 *expr* 分别为 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);
  • 约束条件

    T不是数组。

    返回值

    一个从 args... 构造的类型为 T 的对象的 std::unique_ptr

    示例
  • auto p = allocate_unique<int>(a);

  • auto p = allocate_unique<std::vector<int>>(a, 16, 1);

template<class T, class A>
  std::unique_ptr<T, alloc_deleter<T, A>>
    allocate_unique(const A& a, type_identity_t<T>&& v);
  • 约束条件

    T不是数组。

    返回值

    一个从 v 构造的类型为 T 的对象的 std::unique_ptr

    示例

    auto p = allocate_unique<std::vector<int>>(a, {1, 2});

template<class T, class A>
  std::unique_ptr<T, alloc_deleter<T, A>>
    allocate_unique(const A& a, std::size_t n);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>n 个值初始化对象的序列的 std::unique_ptr

    示例
  • auto p = allocate_unique<double[]>(a, 1024);

  • auto p = allocate_unique<double[][2][2]>(a, 6);

template<class T, class A>
  std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
    allocate_unique(const A& a);
  • 约束条件

    T 是一个边界已知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>extent_v<T> 个值初始化对象的序列的 std::unique_ptr

    示例
  • auto p = allocate_unique<double[1024]>(a);

  • auto p = allocate_unique<double[6][2][2]>(a);

template<class T, class A>
  std::unique_ptr<T, alloc_deleter<T, A>>
    allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>n 个对象序列的 std::unique_ptr,每个对象都初始化为 v

    示例
  • auto p = allocate_unique<double[]>(a, 1024, 1.0);

  • auto p = allocate_unique<double[][2]>(a, 6, {1.0, 0.0});

  • auto p = allocate_unique<std::vector<int>[]>(a, 4, {1, 2});

template<class T, class A>
  std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
    allocate_unique(const A& a, const remove_extent_t<T>& v);
  • 约束条件

    T 是一个边界已知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>extent_v<T> 个对象序列的 std::unique_ptr,每个对象都初始化为 v

    示例
  • auto p = allocate_unique<double[1024]>(a, 1.0);

  • auto p = allocate_unique<double[6][2]>(a, {1.0, 0.0});

  • auto p = allocate_unique<std::vector<int>[4]>(a, {1, 2});

template<class T, class A>
  std::unique_ptr<T, alloc_noinit_deleter<T, A>>
    allocate_unique_noinit(const A& a);
  • 约束条件

    T不是数组。

    返回值

    一个指向类型为 T 的默认初始化对象的 std::unique_ptr

    示例

    auto p = allocate_unique_noinit<double>(a);

template<class T, class A>
  std::unique_ptr<T, alloc_noinit_deleter<T, A>>
    allocate_unique_noinit(const A& a, std::size_t n);
  • 约束条件

    T 是一个边界未知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>n 个默认初始化对象的序列的 std::unique_ptr

    示例

    auto p = allocate_unique_noinit<double[]>(a, 1024);

template<class T, class A>
  std::unique_ptr<remove_extent_t<T>, alloc_noinit_deleter<T, A>>
    allocate_unique_noinit(const A& a);
  • 约束条件

    T 是一个边界已知的数组。

    返回值

    一个指向类型为 remove_extent_t<T>extent_v<T> 个默认初始化对象的序列的 std::unique_ptr

    示例

    auto p = allocate_unique_noinit<double[1024]>(a);

template<class T, class U, class A>
  allocator_pointer_t<allocator_rebind_t<A, remove_cv_t<remove_extent_t<T>>>>
    get_allocator_pointer(const std::unique_ptr<T,
      alloc_deleter<U, A>>& p) noexcept;
  • 返回值

    指向分配的分配器指针。

    示例

    auto r = boost::get_allocator_ptr(p);

删除器

类模板 alloc_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_ptrshared_ptr哪个更适合您的需求并不明显,请先尝试基于shared_ptr的设计。

概要

intrusive_ptr定义在<boost/smart_ptr/intrusive_ptr.hpp>中。

namespace boost {

  template<class T> class intrusive_ptr {
  public:

    typedef T element_type;

    intrusive_ptr() noexcept;
    intrusive_ptr(T * p, bool add_ref = true);

    intrusive_ptr(intrusive_ptr const & r);
    template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);

    intrusive_ptr(intrusive_ptr && r);
    template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);

    ~intrusive_ptr();

    intrusive_ptr & operator=(intrusive_ptr const & r);
    template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
    intrusive_ptr & operator=(T * r);

    intrusive_ptr & operator=(intrusive_ptr && r);
    template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);

    void reset();
    void reset(T * r);
    void reset(T * r, bool add_ref);

    T & operator*() const noexcept;
    T * operator->() const noexcept;
    T * get() const noexcept;
    T * detach() noexcept;

    explicit operator bool () const noexcept;

    void swap(intrusive_ptr & b) noexcept;
  };

  template<class T, class U>
    bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;

  template<class T, class U>
    bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;

  template<class T, class U>
    bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;

  template<class T, class U>
    bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;

  template<class T, class U>
    bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;

  template<class T, class U>
    bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;

  template<class T>
    bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;

  template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;

  template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;

  template<class T, class U>
    intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;

  template<class T, class U>
    intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;

  template<class T, class U>
    intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;

  template<class E, class T, class Y>
    std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
      intrusive_ptr<Y> const & p);
}

成员

element_type

typedef T element_type;

提供模板参数T的类型。

构造函数

intrusive_ptr() noexcept;
  • 后置条件

    get() == 0.

intrusive_ptr(T * p, bool add_ref = true);
  • 效果

    if(p != 0 && add_ref) intrusive_ptr_add_ref(p);.

    后置条件

    get() == p.

intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
  • 效果

    T * p = r.get(); if(p != 0) intrusive_ptr_add_ref(p);.

    后置条件

    get() == r.get().

intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
  • 后置条件

    get() 等于r.get()的旧值。r.get() == 0

析构函数

~intrusive_ptr();
  • 效果

    if(get() != 0) intrusive_ptr_release(get());.

赋值

intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
  • 效果

    等效于intrusive_ptr(r).swap(*this)

    返回值

    *this.

intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
  • 效果

    等效于intrusive_ptr(std::move(r)).swap(*this)

    返回值

    *this.

reset

void reset();
  • 效果

    等效于intrusive_ptr().swap(*this)

void reset(T * r);
  • 效果

    等效于intrusive_ptr(r).swap(*this)

void reset(T * r, bool add_ref);
  • 效果

    等效于intrusive_ptr(r, add_ref).swap(*this)

间接寻址

T & operator*() const noexcept;
  • 需求

    get() != 0.

    返回值

    *get().

T * operator->() const noexcept;
  • 需求

    get() != 0.

    返回值

    get().

get

T * get() const noexcept;
  • 返回值

    存储的指针。

detach

T * detach() noexcept;
  • 返回值

    存储的指针。

    后置条件

    get() == 0.

注意
返回的指针具有更高的引用计数。这允许将intrusive_ptr转换回原始指针,而无需获取和丢弃额外引用的性能开销。可以将其视为非引用递增构造函数的补充。
警告
使用detach会逃脱intrusive_ptr提供的自动引用计数的安全性。仅在绝对必要时(例如,与现有API接口时)才应使用它,并且在充分理解其含义的情况下使用。

转换

explicit operator bool () const noexcept;
  • 返回值

    get() != 0.

    注意
    此转换运算符允许在布尔上下文中使用intrusive_ptr对象,例如if (p && p->valid()) {}
注意
在 C++03 编译器上,返回值的类型未指定。

swap

void swap(intrusive_ptr & b) noexcept;
  • 效果

    交换两个智能指针的内容。

自由函数

比较

template<class T, class U>
  bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
  • 返回值

    a.get() == b.get().

template<class T, class U>
  bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
  • 返回值

    a.get() != b.get().

template<class T, class U>
  bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
  • 返回值

    a.get() == b.

template<class T, class U>
  bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
  • 返回值

    a.get() != b.

template<class T, class U>
  bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
  • 返回值

    a == b.get().

template<class T, class U>
  bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
  • 返回值

    a != b.get().

template<class T>
  bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
  • 返回值

    std::less<T *>()(a.get(), b.get()).

    注意
    允许将intrusive_ptr对象用作关联容器中的键。

swap

template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
  • 效果

    相当于 a.swap(b)

get_pointer

template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
  • 返回值

    p.get().

    注意
    作为泛型编程的辅助工具提供。被mem_fn使用。

static_pointer_cast

template<class T, class U>
  intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
  • 返回值

    intrusive_ptr<T>(static_cast<T*>(r.get())).

const_pointer_cast

template<class T, class U>
  intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;
  • 返回值

    intrusive_ptr<T>(const_cast<T*>(r.get())).

dynamic_pointer_cast

template<class T, class U>
  intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;
  • 返回值

    intrusive_ptr<T>(dynamic_cast<T*>(r.get())).

operator<<

template<class E, class T, class Y>
  std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
    intrusive_ptr<Y> const & p);
  • 效果

    os << p.get();.

    返回值

    os.

intrusive_ref_counter

描述

intrusive_ref_counter类模板实现了一个引用计数器,用于派生用户类,旨在与intrusive_ptr一起使用。基类具有关联的intrusive_ptr_add_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() == p并且local_use_count()等于r的旧计数。r为空且r.get() == 0

unique_ptr构造函数

template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
  • 要求

    Y*应该可以转换为T*

    效果
    • r.get() == 0时,等效于local_shared_ptr()

    • 否则,构造一个拥有shared_ptr<T>( std::move(r) )local_shared_ptr

    抛出

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

    异常安全性

    如果抛出异常,则构造函数无效。

析构函数

~local_shared_ptr() noexcept;
  • 效果
    • 如果*this为空,或者与另一个local_shared_ptr实例共享所有权(local_use_count() > 1),则没有副作用。

    • 否则,销毁拥有的shared_ptr

赋值

local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
  • 效果

    等效于local_shared_ptr(r).swap(*this)

    返回值

    *this.

local_shared_ptr & operator=(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
  • 效果

    等效于local_shared_ptr(std::move(r)).swap(*this)

    返回值

    *this.

local_shared_ptr & operator=(std::nullptr_t) noexcept;
  • 效果

    等效于local_shared_ptr().swap(*this)

    返回值

    *this.

reset

void reset() noexcept;
  • 效果

    等效于local_shared_ptr().swap(*this)

template<class Y> void reset(Y * p);
  • 效果

    等效于local_shared_ptr(p).swap(*this)

template<class Y, class D> void reset(Y * p, D d);
  • 效果

    等效于local_shared_ptr(p, d).swap(*this)

template<class Y, class D, class A> void reset(Y * p, D d, A a);
  • 效果

    等效于local_shared_ptr(p, d, a).swap(*this)

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

    等效于local_shared_ptr(r, p).swap(*this)

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

    等效于local_shared_ptr(std::move(r), p).swap(*this)

间接寻址

T & operator*() const noexcept;
  • 要求

    T不能是数组类型。

    返回值

    *get().

T * operator->() const noexcept;
  • 要求

    T不能是数组类型。

    返回值

    get().

element_type & operator[](std::ptrdiff_t i) const noexcept;
  • 要求

    T应该是数组类型。存储的指针不能为0。i >= 0。如果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;
  • 返回值

    当且仅当*thisr共享所有权或都为空时为true

自由函数

比较

template<class T, class U>
  bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
  bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
  bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
  • 返回值

    a.get() == b.get().

template<class T, class U>
  bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
  bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
  bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
  • 返回值

    a.get() != b.get().

template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
  • 返回值

    p.get() == 0.

template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
  • 返回值

    p.get() != 0.

template<class T, class U>
  bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
  • 返回值

    一个未指定的值,使得

    • operator<是C++标准[lib.alg.sorting]节中描述的严格弱排序;

    • 根据由operator<定义的等价关系!(a < b) && !(b < a),当且仅当两个local_shared_ptr实例共享所有权或两者都为空时,它们才等价。

注意
允许将local_shared_ptr对象用作关联容器中的键。
注意
出于设计原因,省略了其余的比较运算符。

swap

template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
  • 效果

    相当于 a.swap(b)

get_pointer

template<class T>
  typename local_shared_ptr<T>::element_type *
    get_pointer(local_shared_ptr<T> const & p) noexcept;
  • 返回值

    p.get().

    注意
    作为泛型编程的辅助工具提供。被mem_fn使用。

static_pointer_cast

template<class T, class U>
  local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
  • 要求

    表达式static_cast( (U*)0 )必须是良构的。

    返回值

    local_shared_ptr<T>( r, static_cast<typename local_shared_ptr<T>::element_type*>(r.get()) ).

警告
看似等效的表达式local_shared_ptr<T>(static_cast<T*>(r.get()))最终会导致未定义的行为,尝试删除同一个对象两次。

const_pointer_cast

template<class T, class U>
  local_shared_ptr<T> const_pointer_cast(local_shared_ptr<U> const & r) noexcept;
  • 要求

    表达式const_cast( (U*)0 )必须是良构的。

    返回值

    local_shared_ptr<T>( r, const_cast<typename local_shared_ptr<T>::element_type*>(r.get()) ).

dynamic_pointer_cast

template<class T, class U>
    local_shared_ptr<T> dynamic_pointer_cast(local_shared_ptr<U> const & r) noexcept;
  • 要求

    表达式dynamic_cast( (U*)0 )必须是良构的。

    返回值
    • dynamic_cast<typename local_shared_ptr<T>::element_type*>(r.get())返回一个非零值p时,local_shared_ptr<T>(r, p)

    • 否则,local_shared_ptr<T>()

reinterpret_pointer_cast

template<class T, class U>
  local_shared_ptr<T> reinterpret_pointer_cast(local_shared_ptr<U> const & r) noexcept;
  • 要求

    表达式reinterpret_cast( (U*)0 )必须是良构的。

    返回值

    local_shared_ptr<T>( r, reinterpret_cast<typename local_shared_ptr<T>::element_type*>(r.get()) ).

operator<<

template<class E, class T, class Y>
  std::basic_ostream<E, T> &
    operator<< (std::basic_ostream<E, T> & os, local_shared_ptr<Y> const & p);
  • 效果

    os << p.get();.

    返回值

    os.

get_deleter

template<class D, class T>
  D * get_deleter(local_shared_ptr<T> const & p) noexcept;
  • 返回值

    如果*this拥有一个shared_ptr实例p,则为get_deleter<D>( p ),否则为0。

make_local_shared:创建 local_shared_ptr

描述

函数模板make_local_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);

指针转换函数模板是针对原始指针、std::shared_ptrstd::unique_ptrstatic_pointer_castdynamic_pointer_castconst_pointer_castreinterpret_pointer_cast的重载。这样,在开发与指针类型无关的类(例如内存管理器或与共享内存兼容的类)时,可以对原始指针和智能指针使用相同的代码。

概要

泛型指针转换定义在<boost/pointer_cast.hpp>中。

namespace boost {
  template<class T, class U> T* static_pointer_cast(U* p) noexcept;
  template<class T, class U> T* dynamic_pointer_cast(U* p) noexcept;
  template<class T, class U> T* const_pointer_cast(U* p) noexcept;
  template<class T, class U> T* reinterpret_pointer_cast(U* p) noexcept;

  template<class T, class U> std::shared_ptr<T>
    static_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  template<class T, class U> std::shared_ptr<T>
    dynamic_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  template<class T, class U> std::shared_ptr<T>
    const_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  template<class T, class U> std::shared_ptr<T>
    reinterpret_pointer_cast(const std::shared_ptr<U>& p) noexcept;

  template<class T, class U> std::unique_ptr<T>
    static_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  template<class T, class U> std::unique_ptr<T>
    dynamic_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  template<class T, class U> std::unique_ptr<T>
    const_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  template<class T, class U> std::unique_ptr<T>
    reinterpret_pointer_cast(std::unique_ptr<U>&& p) noexcept;
}

自由函数

static_pointer_cast

template<class T, class U> T* static_pointer_cast(U* p) noexcept;
  • 返回值

    static_cast<T*>(p)

template<class T, class U> std::shared_ptr<T>
  static_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  • 返回值

    std::static_pointer_cast<T>(p)

template<class T, class U> std::unique_ptr<T>
  static_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  • 要求

    表达式static_cast<T*>((U*)0)必须格式良好。

    返回值

    std::unique_ptr<T>(static_cast<typename std::unique_ptr<T>::element_type*>(p.release())).

警告
看似等效的表达式std::unique_ptr<T>(static_cast<T*>(p.get()))最终会导致未定义的行为,尝试删除同一个对象两次。

dynamic_pointer_cast

template<class T, class U> T* dynamic_pointer_cast(U* p) noexcept;
  • 返回值

    dynamic_cast<T*>(p)

template<class T, class U> std::shared_ptr<T>
  dynamic_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  • 返回值

    std::dynamic_pointer_cast<T>(p)

template<class T, class U> std::unique_ptr<T>
  dynamic_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  • 要求
  • 表达式static_cast<T*>((U*)0)必须格式良好。

  • T必须具有虚析构函数。

    返回值
  • dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.get())返回一个非零值时,std::unique_ptr<T>(dynamic_cast<typename std::unique_ptr<T>::element_type*>(p.release()));

  • 否则,std::unique_ptr<T>()

const_pointer_cast

template<class T, class U> T* const_pointer_cast(U* p) noexcept;
  • 返回值

    const_cast<T*>(p)

template<class T, class U> std::shared_ptr<T>
  const_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  • 返回值

    std::const_pointer_cast<T>(p)

template<class T, class U> std::unique_ptr<T>
  const_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  • 要求

    表达式const_cast<T*>((U*)0)必须格式良好。

    返回值

    std::unique_ptr<T>(const_cast<typename std::unique_ptr<T>::element_type*>(p.release())).

reinterpret_pointer_cast

template<class T, class U> T* reinterpret_pointer_cast(U* p) noexcept;
  • 返回值

    reinterpret_cast<T*>(p)

template<class T, class U> std::shared_ptr<T>
  reinterpret_pointer_cast(const std::shared_ptr<U>& p) noexcept;
  • 返回值

    std::reinterpret_pointer_cast<T>(p)

template<class T, class U> std::unique_ptr<T>
  reinterpret_pointer_cast(std::unique_ptr<U>&& p) noexcept;
  • 要求

    表达式reinterpret_cast<T*>((U*)0)必须格式良好。

    返回值

    std::unique_ptr<T>(reinterpret_cast<typename std::unique_ptr<T>::element_type*>(p.release())).

示例

以下示例演示了泛型指针转换如何帮助我们创建指针无关代码。

#include <boost/pointer_cast.hpp>
#include <boost/shared_ptr.hpp>

class base {
public:
  virtual ~base() { }
};

class derived : public base { };

template<class Ptr>
void check_if_it_is_derived(const Ptr& ptr)
{
  assert(boost::dynamic_pointer_cast<derived>(ptr) != 0);
}

int main()
{
  base* ptr = new derived;
  boost::shared_ptr<base> sptr(new derived);

  check_if_it_is_derived(ptr);
  check_if_it_is_derived(sptr);

  delete ptr;
}

pointer_to_other

描述

pointer_to_other 实用程序提供了一种方法,给定一个源指针类型,可以获得指向另一个被指向类型相同类型的指针。

测试/示例代码位于 pointer_to_other_test.cpp

基本原理

在构建指针无关类(如内存管理器、分配器或容器)时,通常需要泛型地定义指针,这样如果模板参数表示一个指针(例如,指向int 的原始指针或智能指针),我们可以定义另一个指向另一个被指向类型(指向float 的原始指针或智能指针)的相同类型的指针。

template <class IntPtr> class FloatPointerHolder
{
    // Let's define a pointer to a float

    typedef typename boost::pointer_to_other
        <IntPtr, float>::type float_ptr_t;

    float_ptr_t float_ptr;
};

概要

pointer_to_other 定义在<boost/smart_ptr/pointer_to_other.hpp>中。

namespace boost {

  template<class T, class U> struct pointer_to_other;

  template<class T, class U,
    template <class> class Sp>
      struct pointer_to_other< Sp<T>, U >
  {
    typedef Sp<U> type;
  };

  template<class T, class T2, class U,
    template <class, class> class Sp>
      struct pointer_to_other< Sp<T, T2>, U >
  {
    typedef Sp<U, T2> type;
  };

  template<class T, class T2, class T3, class U,
    template <class, class, class> class Sp>
      struct pointer_to_other< Sp<T, T2, T3>, U >
  {
    typedef Sp<U, T2, T3> type;
  };

  template<class T, class U>
    struct pointer_to_other< T*, U >
  {
    typedef U* type;
  };
}

如果这些定义对于特定智能指针不正确,我们可以定义pointer_to_other 的特化。

示例

// Let's define a memory allocator that can
// work with raw and smart pointers

#include <boost/pointer_to_other.hpp>

template <class VoidPtr>
class memory_allocator
{
    // Predefine a memory_block

    struct block;

    // Define a pointer to a memory_block from a void pointer
    // If VoidPtr is void *, block_ptr_t is block*
    // If VoidPtr is smart_ptr<void>, block_ptr_t is smart_ptr<block>

    typedef typename boost::pointer_to_other
        <VoidPtr, block>::type block_ptr_t;

    struct block
    {
        std::size_t size;
        block_ptr_t next_block;
    };

    block_ptr_t free_blocks;
};

正如我们所看到的,使用pointer_to_other,我们可以创建指针无关代码。

atomic_shared_ptr

描述

类模板atomic_shared_ptr<T> 为类型为shared_ptr<T> 的包含值实现了std::atomic 的接口。对atomic_shared_ptr 的并发访问不是数据竞争。

概要

atomic_shared_ptr 定义在<boost/smart_ptr/atomic_shared_ptr.hpp>中。

namespace boost {

  template<class T> class atomic_shared_ptr {
  private:

    shared_ptr<T> p_; // exposition only

    atomic_shared_ptr(const atomic_shared_ptr&) = delete;
    atomic_shared_ptr& operator=(const atomic_shared_ptr&) = delete;

  public:

    constexpr atomic_shared_ptr() noexcept;
    atomic_shared_ptr( shared_ptr<T> p ) noexcept;

    atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;

    bool is_lock_free() const noexcept;

    shared_ptr<T> load( int = 0 ) const noexcept;
    operator shared_ptr<T>() const noexcept;

    void store( shared_ptr<T> r, int = 0 ) noexcept;

    shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;

    bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
    bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
    bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
    bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;

    bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
    bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
    bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
    bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
  };
}

成员

constexpr atomic_shared_ptr() noexcept;
  • 效果

    默认初始化p_

atomic_shared_ptr( shared_ptr<T> p ) noexcept;
  • 效果

    p_ 初始化为p

atomic_shared_ptr& operator=( shared_ptr<T> r ) noexcept;
  • 效果

    p_.swap(r).

    返回值

    *this.

bool is_lock_free() const noexcept;
  • 返回值

    false.

    注意
    此实现不是无锁的。
shared_ptr<T> load( int = 0 ) const noexcept;
operator shared_ptr<T>() const noexcept;
  • 返回值

    p_.

    注意
    int 参数旨在为memory_order 类型,但被忽略。此实现基于锁,因此始终顺序一致。
void store( shared_ptr<T> r, int = 0 ) noexcept;
  • 效果

    p_.swap(r).

shared_ptr<T> exchange( shared_ptr<T> r, int = 0 ) noexcept;
  • 效果

    p_.swap(r).

    返回值

    p_ 的旧值。

bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, const shared_ptr<T>& w, int = 0 ) noexcept;
  • 效果

    如果p_ 等于v,则将w 赋值给p_,否则将p_ 赋值给v

    返回值

    如果p_ 等于v,则为true,否则为false

    备注

    如果两个shared_ptr实例存储相同的指针值并共享所有权,则它们是等效的。

bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_weak( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int, int ) noexcept;
bool compare_exchange_strong( shared_ptr<T>& v, shared_ptr<T>&& w, int = 0 ) noexcept;
  • 效果

    如果p_ 等于v,则将std::move(w) 赋值给p_,否则将p_ 赋值给v

    返回值

    如果p_ 等于v,则为true,否则为false

    备注

    无论哪种情况,w 的旧值都不会保留。

owner_less

描述

owner_less<T> 是一个辅助函数对象,使用owner_before 比较两个智能指针对象。它仅为了与 C++11 保持兼容而提供,对应于同名标准组件。

使用 Boost 智能指针时,无需使用owner_less,因为提供的operator< 重载(以及相应的std::less)返回相同的结果。

概要

owner_less 定义在<boost/smart_ptr/owner_less.hpp>中。

namespace boost {

  template<class T = void> struct owner_less
  {
    typedef bool result_type;
    typedef T first_argument_type;
    typedef T second_argument_type;

    template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
  };
}

成员

template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
  • 返回值

    u.owner_before( v ).

owner_equal_to

描述

owner_equal_to<T> 是一个辅助函数对象,使用owner_equals 比较两个智能指针对象。

概要

owner_equal_to 定义在<boost/smart_ptr/owner_equal_to.hpp>中。

namespace boost {

  template<class T = void> struct owner_equal_to
  {
    typedef bool result_type;
    typedef T first_argument_type;
    typedef T second_argument_type;

    template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
  };
}

成员

template<class U, class V> bool operator()( U const & u, V const & v ) const noexcept;
  • 返回值

    u.owner_equals( v ).

owner_hash

描述

owner_hash<T> 是一个辅助函数对象,它接受一个智能指针p 并返回p.owner_hash_value()。它对于创建使用基于所有权的相等性(而不是默认的指针值相等性)的shared_ptr 的无序容器很有用。(它也可以与weak_ptr 一起使用,但没有必要,因为boost::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() 递减计数,当计数降为零时销毁自身。

可以在shared_ptr 中保存指向 COM 对象的指针。

shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p)
{
    p->AddRef();
    shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release));
    return pw;
}

但是,请注意,从pw 创建的shared_ptr 复制不会在 COM 对象的嵌入式计数中“注册”;它们将共享在make_shared_from_COM 中创建的单个引用。从pw 创建的弱指针在最后一个shared_ptr 被销毁时将失效,无论 COM 对象本身是否仍然存活。

mem_fn 文档中所述,你需要首先#define BOOST_MEM_FN_ENABLE_STDCALL

使用 shared_ptr 保存指向具有嵌入式引用计数的对象的指针

这是上述技术的概括。该示例假设该对象实现了intrusive_ptr 所需的两个函数,intrusive_ptr_add_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;
    }
};

解决方案是在impl 中将指向this 的弱指针作为成员保留。

class impl: public X, public Y
{
private:

    weak_ptr<impl> weak_this;

    impl(impl const &);
    impl & operator=(impl const &);

    impl() { ... }

public:

    static shared_ptr<impl> create()
    {
        shared_ptr<impl> pi(new impl);
        pi->weak_this = pi;
        return pi;
    }

    virtual void f() { ... }

    virtual shared_ptr<X> getX()
    {
        shared_ptr<X> px(weak_this);
        return px;
    }
};

该库现在包含一个辅助类模板enable_shared_from_this,可用于封装解决方案。

class impl: public X, public Y, public enable_shared_from_this<impl>
{
public:

    impl(impl const &);
    impl & operator=(impl const &);

public:

    virtual void f() { ... }

    virtual shared_ptr<X> getX()
    {
        return shared_from_this();
    }
}

请注意,您不再需要手动初始化enable_shared_from_this 中的weak_ptr 成员。构造指向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 保存任意对象

shared_ptr<void> 可以充当类似于void* 的通用对象指针。当构造为

shared_ptr<void> pv(new X);

shared_ptr<void> 实例被销毁时,它将通过执行~X 正确地处理X 对象。

此属性的使用方式与使用原始void* 从对象指针中暂时剥离类型信息的方式大致相同。shared_ptr<void> 稍后可以使用static_pointer_cast 转换回正确的类型。

将任意数据与异构 shared_ptr 实例关联

shared_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 作为可复制互斥锁

有时需要从函数返回互斥锁,而不可复制的锁无法按值返回。可以使用 shared_ptr 作为互斥锁。

class mutex
{
public:

    void lock();
    void unlock();
};

shared_ptr<mutex> lock(mutex & m)
{
    m.lock();
    return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}

更好的是,充当锁的 shared_ptr 实例可以封装在一个专用的 shared_lock 类中。

class shared_lock
{
private:

    shared_ptr<void> pv;

public:

    template<class Mutex> explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {}
};

shared_lock 现在可以这样使用:

shared_lock lock(m);

请注意,由于 shared_ptr<void> 能够隐藏类型信息,因此 shared_lock 不是根据互斥锁类型进行模板化的。

使用 shared_ptr 包装成员函数调用

shared_ptr 实现 Bjarne Stroustrup 的文章“Wrapping C++ Member Function Calls”(可在网上找到:[http://www.stroustrup.com/wrapper.pdf](http://www.stroustrup.com/wrapper.pdf))中描述的 Wrap/CallProxy 方案所需的拥有权语义。下面给出一个实现。

template<class T> class pointer
{
private:

    T * p_;

public:

    explicit pointer(T * p): p_(p)
    {
    }

    shared_ptr<T> operator->() const
    {
        p_->prefix();
        return shared_ptr<T>(p_, mem_fn(&T::suffix));
    }
};

class X
{
private:

    void prefix();
    void suffix();
    friend class pointer<X>;

public:

    void f();
    void g();
};

int main()
{
    X x;

    pointer<X> px(&x);

    px->f();
    px->g();
}

延迟释放

在某些情况下,单个 px.reset() 可能会在性能关键区域触发代价高昂的释放操作。

class X; // ~X is expensive

class Y
{
    shared_ptr<X> px;

public:

    void f()
    {
        px.reset();
    }
};

解决方案是通过将 px 移动到一个专用的空闲列表中来推迟潜在的释放操作,该空闲列表可以在性能和响应时间不是问题时定期清空。

vector< shared_ptr<void> > free_list;

class Y
{
    shared_ptr<X> px;

public:

    void f()
    {
        free_list.push_back(px);
        px.reset();
    }
};

// periodically invoke free_list.clear() when convenient

另一种方法是通过使用延迟删除器将空闲列表逻辑移动到构造点。

struct delayed_deleter
{
    template<class T> void operator()(T * p)
    {
        try
        {
            shared_ptr<void> pv(p);
            free_list.push_back(pv);
        }
        catch(...)
        {
        }
    }
};

指向未由 shared_ptr 管理的对象的弱指针

使对象持有指向自身的 shared_ptr,使用 null_deleter

class X
{
private:

    shared_ptr<X> this_;
    int i_;

public:

    explicit X(int i): this_(this, null_deleter()), i_(i)
    {
    }

    // repeat in all constructors (including the copy constructor!)

    X(X const & rhs): this_(this, null_deleter()), i_(rhs.i_)
    {
    }

    // do not forget to not assign this_ in the copy assignment

    X & operator=(X const & rhs)
    {
        i_ = rhs.i_;
    }

    weak_ptr<X> get_weak_ptr() const { return this_; }
};

当对象的生存期结束时,X::this_ 将被销毁,所有弱指针将自动过期。

附录 B:历史和致谢

1994 年夏季

Greg Colvin [建议](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1994/N0555.pdf) C++ 标准委员会采用名为 auto_ptrcounted_ptr 的类,它们与我们现在称之为 scoped_ptrshared_ptr 的类非常相似。在库工作组的建议未被全体委员会采纳的极少数情况下之一,counted_ptr 被拒绝,并且向 auto_ptr 添加了令人惊讶的拥有权转移语义。

1998 年 10 月

Beman Dawes 建议在 Per Andersson、Matt Austern、Greg Colvin、Sean Corfield、Pete Becker、Nico Josuttis、Dietmar Kühl、Nathan Myers、Chichiang Wan 和 Judy Ward 的会议上,使用 safe_ptrcounted_ptr 的名称恢复原始语义。在讨论期间,最终确定了四个新的类名,决定不需要完全遵循 std::auto_ptr 接口,并且最终确定了各种函数签名和语义。

在接下来的三个月中,考虑了 shared_ptr 的几种实现方案,并在 [boost.org](/) 邮件列表中进行了讨论。实现问题围绕着必须保留的引用计数展开,它可以附加到指向的对象上,也可以在其他地方分离。这些变体中的每一个本身都有两个主要变体。

  • 直接分离:shared_ptr 包含指向对象的指针和指向计数的指针。

  • 间接分离:shared_ptr 包含指向辅助对象的指针,该辅助对象又包含指向对象和计数的指针。

  • 嵌入式附加:计数是所指向对象的成员。

  • 位置附加:计数通过 operator new 操作附加。

每种实现技术都有其优点和缺点。我们甚至对直接和间接方法进行了各种计时测试,发现至少在英特尔奔腾芯片上,几乎没有可测量的差异。Kevlin Henney 提供了他撰写的关于“计数体技术”的论文。Dietmar Kühl 建议了一种优雅的部分模板特化技术,允许用户选择他们喜欢的实现,并且也对此进行了实验。

但 Greg Colvin 和 Jerry Schwarz 认为“参数化会劝退用户”,最终我们只选择提供直接实现。

1999 年 5 月

1999 年 4 月和 5 月,Valentin Bonnard 和 David Abrahams 提出了许多建议,导致了许多改进。

1999 年 9 月

Luis Coelho 提供了 shared_ptr::swapshared_array::swap

1999 年 11 月

Darin Adler 提供了 operator ==operator !=std::swap,以及共享类型的 std::less 特化。

2001 年 5 月

Vladimir Prus 建议在销毁时需要一个完整类型。改进是在包括 Dave Abrahams、Greg Colvin、Beman Dawes、Rainer Deyke、Peter Dimov、John Maddock、Vladimir Prus、Shankar Sai 和其他人在内的讨论中发展起来的。

2002 年 1 月

Peter Dimov 重做了所有四个类,添加了功能,修复了错误,将它们拆分为四个单独的头文件,并添加了 weak_ptr

2003 年 3 月

Peter Dimov、Beman Dawes 和 Greg Colvin [建议](http://open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html) 通过第一个库技术报告(称为 TR1)将 shared_ptrweak_ptr 包含到标准库中。该提案被接受,并最终成为 2011 年 C++ 标准的一部分。

2007 年 7 月

Peter Dimov 和 Beman Dawes [建议](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2351.htm) 对 shared_ptr 进行了一些增强,因为它进入了最终成为 C++11 标准的工作文档。

2012 年 11 月

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

Peter Dimov 通过扩展 shared_ptr 以支持 shared_ptr<T[]>shared_ptr<T[N]> 语法来辅助此开发。

2013 年 4 月

Peter Dimov [建议](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3640.html) 将 shared_ptr 的扩展以支持数组包含到标准中,并且它被接受了。

2014 年 2 月

Glen Fernandes 更新了 make_sharedallocate_shared 以符合 C++ 标准论文 [N3870](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3870.html) 中的规范,并为数组和对象实现了 make_unique

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

2017 年 2 月

Glen Fernandes 重写了数组的 allocate_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_ptr 指向 T[]T[N],并且在各个方面都更好。

描述

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

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

通常,shared_array 无法正确保存指向已使用非数组形式的 new 分配的对象的指针。有关此用法的说明,请参阅 shared_ptr

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

指向 std::vectorshared_ptrshared_array 的替代方案,它功能更强大,但灵活性更高。

类模板的参数是 T,即指向的对象的类型。shared_array 及其大多数成员函数对 T 没有要求;它可以是不完整类型或 void。对 T 有附加要求的成员函数(构造函数、reset)在下面有明确说明。

概要

namespace boost {

  template<class T> class shared_array {
  public:
    typedef T element_type;

    explicit shared_array(T* p = 0);
    template<class D> shared_array(T* p, D d);
    shared_array(const shared_array& v) noexcept;

    ~shared_array() noexcept;

    shared_array& operator=(const shared_array& v) noexcept;

    void reset(T* p = 0);
    template<class D> void reset(T* p, D d);

    T& operator[](std::ptrdiff_t n) const noexcept;
    T* get() const noexcept;

    bool unique() const noexcept;
    long use_count() const noexcept;

    explicit operator bool() const noexcept;

    void swap(shared_array<T>& v) noexcept;
  };

  template<class T> bool
    operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
  template<class T> bool
    operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
  template<class T> bool
    operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;

  template<class T>
    void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
}

成员

element_type

typedef T element_type;
类型

提供存储指针的类型。

构造函数

explicit shared_array(T* p = 0);
  • 效果

    构造一个 shared_array,存储 p 的副本,p 必须是指向通过 C++ new[] 表达式分配的数组的指针或为 0。之后,使用计数为 1(即使 p == 0;参见 ~shared_array)。

    要求

    T 是完整类型。

    抛出

    std::bad_alloc。如果抛出异常,则调用 delete[] p

template<class D> shared_array(T* p, D d);
  • 效果

    构造一个 shared_array,存储 pd 的副本。之后,使用计数为 1。当需要删除 p 指向的数组时,在语句 d(p) 中使用对象 d

    要求
  • T 是完整类型。

  • D 的复制构造函数和析构函数不能抛出异常。

  • 使用参数 p 调用对象 d 不能抛出异常。

    抛出

    std::bad_alloc。如果抛出异常,则调用 d(p)

shared_array(const shared_array& v) noexcept;
  • 效果

    构造一个 shared_array,就像存储 v 中存储的指针的副本一样。之后,所有副本的使用计数都比初始使用计数多 1。

    要求

    T 是完整类型。

析构函数

~shared_array() noexcept;
  • 效果

    递减使用计数。然后,如果使用计数为 0,则删除存储的指针指向的数组。请注意,对值为 0 的指针进行 delete[] 操作是无害的。

赋值

shared_array& operator=(const shared_array& v) noexcept;
  • 效果

    构造一个如上所述的新 shared_array,然后用新 shared_array 替换此 shared_array,销毁被替换的对象。

    要求

    T 是完整类型。

    返回值

    *this.

reset

void reset(T* p = 0);
  • 效果

    构造一个如上所述的新 shared_array,然后用新 shared_array 替换此 shared_array,销毁被替换的对象。

    要求

    T 是完整类型。

    抛出

    std::bad_alloc。如果抛出异常,则调用 delete[] p

template<class D> void reset(T* p, D d);
  • 效果

    构造一个如上所述的新 shared_array,然后用新 shared_array 替换此 shared_array,销毁被替换的对象。

    要求
  • T 是完整类型。

  • D 的复制构造函数不能抛出异常。

    抛出

    std::bad_alloc。如果抛出异常,则调用 d(p)

索引

T& operator[](std::ptrdiff_t n) const noexcept;
返回值

对存储的指针指向的数组的元素 n 的引用。如果存储的指针为 0,或者 n 小于 0 或大于等于数组中的元素数,则行为未定义,几乎肯定是不希望的。

要求

T 是完整类型。

get

T* get() const noexcept;
  • 返回值

    存储的指针。

唯一性

bool unique() const noexcept;
  • 返回值

    如果没有任何其他 shared_array 共享存储指针的所有权,则为 true,否则为 false

use_count

long use_count() const noexcept;
  • 返回值

    共享存储指针所有权的 shared_array 对象的数量。

转换

explicit operator bool() const noexcept;
  • 返回值

    get() != 0.

    要求

    T 是完整类型。

swap

void swap(shared_array<T>& b) noexcept;
  • 效果

    交换两个智能指针的内容。

自由函数

比较

template<class T> bool
  operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
  operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
  operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
  • 返回值

    比较两个智能指针的存储指针的结果。

注意
提供operator<重载是为了定义排序规则,以便shared_array对象可以用于关联容器,例如std::map。实现使用std::less<T*>进行比较。这确保了比较能够正确处理,因为标准规定指针上的关系运算符是未指定的(5.9 [expr.rel] 第2段),但指针上的std::less是明确定义的(20.3.3 [lib.comparisons] 第8段)。

swap

template<class T>
  void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
  • 返回值

    a.swap(b).

    要求

    T 是完整类型。

本文档

  • 版权所有 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