Boost C++ 函式庫

...世界上最受推崇且設計精良的 C++ 函式庫專案之一。 Herb SutterAndrei Alexandrescu, C++ 程式碼標準

簡介

智慧指標是儲存指向動態配置(堆積)物件之指標的物件。它們的行為很像內建的 C++ 指標,除了它們會在適當的時間自動刪除指向的物件。智慧指標在出現例外狀況時特別有用,因為它們可確保動態配置物件的正確解構。它們也可以用來追蹤多個擁有者共用的動態配置物件。

從概念上講,智慧指標被視為擁有指向的物件,因此負責在不再需要時刪除該物件。因此,它們是 Bjarne Stroustrup 的「The C++ Programming Language」第三版,第 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,一個輔助基底類別,可讓您取得指向 thisshared_ptr

  • enable_shared_from,一個較新且更好的 enable_shared_from_this 替代方案;

  • pointer_to_other,一個用於將一個智慧指標類型轉換為另一個智慧指標類型的輔助特性;

  • static_pointer_cast 和同伴,通用智慧指標轉換;

  • intrusive_ref_counter,一個包含參考計數的輔助基底類別。

  • atomic_shared_ptr,一個輔助類別,實作 std::atomic 的介面,用於 shared_ptr 類型的值。

一般而言,不允許該函式庫中指標管理的物件的解構子或 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<T>::empty()

  • 新增 enable_shared_fromshared_fromweak_from

1.65.0 版的變更

  • 新增 atomic_shared_ptr

  • 新增 local_shared_ptrmake_local_shared

scoped_ptr:作用域物件擁有權

描述

scoped_ptr 類別範本儲存指向動態配置物件的指標。(動態配置物件是使用 C++ new 運算式配置的。)保證所指向的物件會被刪除,無論是在 scoped_ptr 解構時,還是透過明確的 reset。請參閱範例

scoped_ptr 是簡單需求的簡單解決方案。它提供了一個基本的「資源取得即初始化」設施,沒有共享所有權或轉移所有權語意。它的名稱和語意強制執行(透過不可複製)表示其意圖僅在目前範圍內保留所有權。由於它是不可複製的,因此對於不應複製的指標來說,它比 shared_ptr 更安全。

由於 scoped_ptr 很簡單,因此在通常的實作中,每個操作都與內建指標一樣快,並且空間負擔不超過內建指標。

scoped_ptr 不能在 C++ 標準函式庫容器中使用。如果您需要可以使用的智慧指標,請使用 shared_ptrstd::unique_ptr

scoped_ptr 無法正確保存指向動態配置陣列的指標。請參閱 scoped_array 以了解該用法。

類別範本以 T 參數化,這是指向的物件類型。摧毀 T 不得擲回例外狀況,且 T 必須在 scoped_ptr<T>::~scoped_ptr 實例化時完成。

概要

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 的副本,該副本必須透過 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 上運作。

Handle/Body 慣用法

scoped_ptr 的一個常見用法是實作避免在標頭檔中公開主體(實作)的 handle/body(也稱為 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 作為參數,T 是指向的物件類型。

概要

scoped_array 定義於 <boost/smart_ptr/scoped_array.hpp>

namespace boost {

  template<class T> class scoped_array {
  private:

    scoped_array(scoped_array const &);
    scoped_array & operator=(scoped_array const &);

    void operator==( scoped_array const& ) const;
    void operator!=( scoped_array const& ) const;

  public:

    typedef T element_type;

    explicit scoped_array(T * p = 0) noexcept;
    ~scoped_array() noexcept;

    void reset(T * p = 0) noexcept;

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

    explicit operator bool () const noexcept;

    void swap(scoped_array & b) noexcept;
  };

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

  template<class T>
    bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
  template<class T>
    bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;

  template<class T>
    bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
  template<class T>
    bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
}

成員

element_type

typedef T element_type;

提供已儲存指標的類型。

建構子

explicit scoped_array(T * p = 0) noexcept;

建構一個 scoped_array,儲存 p 的副本,p 必須透過 C++ 的 new[] 表達式配置或為 0。T 不必是完整類型。

解構子

~scoped_array() noexcept;

刪除儲存指標所指向的陣列。請注意,對值為 0 的指標執行 delete[] 是無害的。T 必須是完整類型,且對儲存指標執行 delete[] 不得擲回例外。

reset

void reset(T * p = 0) noexcept;

刪除儲存指標所指向的陣列,然後儲存 p 的副本,p 必須透過 C++ 的 new[] 表達式配置或為 0。T 必須是完整類型,且對儲存指標執行 delete[] 不得擲回例外。

下標

T & operator[](std::ptrdiff_t i) const noexcept;

傳回儲存指標所指向陣列中索引 i 元素的參照。如果儲存的指標為 0,或者 i 小於 0 或大於等於陣列中的元素數,則行為未定義且幾乎肯定是不希望發生的。

get

T * get() const noexcept;

傳回儲存的指標。T 不需要是完整的類型。

轉換

explicit operator bool () const noexcept;

傳回 get() != 0

注意
在 C++03 編譯器上,傳回值是不指定的類型。

swap

void swap(scoped_array & b) noexcept;

交換兩個智慧指標的內容。T 不需要是完整的類型。

自由函式

swap

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

等同於 a.swap(b)

比較

template<class T>
  bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
  bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;

傳回 p.get() == nullptr

template<class T>
  bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
  bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;

傳回 p.get() != nullptr

shared_ptr:共享擁有權

描述

shared_ptr 類別樣板儲存一個指向動態配置物件的指標,通常使用 C++ 的 new 表達式。當最後一個指向它的 shared_ptr 被銷毀或重設時,保證指向的物件會被刪除。

程式碼範例 1. 使用 shared_ptr
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );

shared_ptr 會刪除在建構時傳遞的確切指標,包括其原始類型,無論樣板參數為何。在上面的第二個範例中,當 p2 被銷毀或重設時,它會對傳遞給建構子的原始 int* 呼叫 delete,即使 p2 本身是 shared_ptr<void> 類型,並儲存一個 void* 類型的指標。

每個 shared_ptr 都符合 C++ 標準函式庫的 CopyConstructibleMoveConstructibleCopyAssignableMoveAssignable 的要求,並且可以用於標準函式庫容器中。提供比較運算子,使 shared_ptr 可以與標準函式庫的關聯容器搭配使用。

由於實作使用參考計數,shared_ptr 實例的循環將不會被回收。例如,如果 main() 持有指向 Ashared_ptr,而 A 直接或間接地持有指向 Ashared_ptr,則 A 的使用計數將為 2。銷毀原始的 shared_ptr 將會使 A 懸空,其使用計數為 1。使用 weak_ptr 來「打破循環」。

類別樣板以 T 作為參數,T 是指向的物件類型。shared_ptr 及其大多數成員函式對 T 沒有任何要求;它可以是不完整類型或 void。有額外要求的成員函式(建構子、reset)將在下面明確說明。

只要 T* 可以隱式轉換為 U*shared_ptr<T> 就可以隱式轉換為 shared_ptr<U>。特別是,shared_ptr<T> 可以隱式轉換為 shared_ptr<T const>,轉換為 UT 可存取基底的 shared_ptr<U>,以及轉換為 shared_ptr<void>

shared_ptr 現在是 C++11 標準的一部分,作為 std::shared_ptr

從 Boost 1.53 版開始,shared_ptr 可以用來持有指向動態配置陣列的指標。這是透過使用陣列類型(T[]T[N])作為樣板參數來完成的。使用未調整大小的陣列 T[] 與調整大小的陣列 T[N] 之間幾乎沒有差異;後者只是使 operator[] 可以對索引執行範圍檢查。

程式碼範例 2. 使用 shared_ptr 與陣列
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );

最佳實踐

一個幾乎消除記憶體洩漏可能性的簡單指南是:始終使用具名的智慧型指標變數來持有 new 的結果。程式碼中每個出現的 new 關鍵字都應具有以下形式

shared_ptr<T> p(new Y);

當然,可以接受使用另一個智慧型指標來取代上面的 shared_ptr;讓 TY 為相同類型,或將引數傳遞給 Y 的建構子也是可以的。

如果您遵守此指南,自然而然地,您將不會有明確的 delete 陳述式;try/catch 結構會很少見。

避免使用未命名的 shared_ptr 暫時物件來節省輸入;若要了解為何這很危險,請考慮此範例

程式碼範例 3. 使用 shared_ptr 的異常安全和不安全用法
void f(shared_ptr<int>, int);
int g();

void ok()
{
    shared_ptr<int> p( new int(2) );
    f( p, g() );
}

void bad()
{
    f( shared_ptr<int>( new int(2) ), g() );
}

函式 ok 完全遵循指南,而 bad 原地建構暫時的 shared_ptr,導致可能發生記憶體洩漏。由於函式引數是以未指定的順序評估,因此有可能先評估 new int(2),第二個評估 g(),如果 g 擲回例外,我們可能永遠不會到達 shared_ptr 建構子。有關更多資訊,請參閱 Herb Sutter 的處理方式

也可以透過使用 <boost/smart_ptr/make_shared.hpp> 中定義的 make_sharedallocate_shared 工廠函式來消除上述的異常安全問題。這些工廠函式還可以透過合併配置來提供效率上的好處。

概要

shared_ptr 定義於 <boost/smart_ptr/shared_ptr.hpp>

namespace boost {

  class bad_weak_ptr: public std::exception;

  template<class T> class weak_ptr;

  template<class T> class shared_ptr {
  public:

    typedef /*see below*/ element_type;

    constexpr shared_ptr() noexcept;
    constexpr shared_ptr(std::nullptr_t) noexcept;

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

    ~shared_ptr() noexcept;

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

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

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

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

    template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
    template<class Y> shared_ptr(std::auto_ptr<Y> && r);

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

    shared_ptr & operator=(shared_ptr const & r) noexcept;
    template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;

    shared_ptr & operator=(shared_ptr const && r) noexcept;
    template<class Y> shared_ptr & operator=(shared_ptr<Y> const && r) noexcept;

    template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
    template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);

    template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);

    shared_ptr & operator=(std::nullptr_t) noexcept;

    void reset() noexcept;

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

    template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
    template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;

    T & operator*() const noexcept; // only valid when T is not an array type
    T * operator->() const noexcept; // only valid when T is not an array type

    // only valid when T is an array type
    element_type & operator[](std::ptrdiff_t i) const noexcept;

    element_type * get() const noexcept;

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

    explicit operator bool() const noexcept;

    void swap(shared_ptr & b) noexcept;

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

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

    std::size_t owner_hash_value() const noexcept;
  };

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

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

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

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

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

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

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

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

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

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

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

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

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

  template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;

  template<class T> shared_ptr<T> atomic_load( shared_ptr<T> const * p ) noexcept;
  template<class T>
    shared_ptr<T> atomic_load_explicit( shared_ptr<T> const * p, int ) noexcept;

  template<class T>
    void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
  template<class T>
    void atomic_store_explicit( shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;

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

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

成員

element_type

typedef ... element_type;

T 不是陣列類型時,element_typeT,當 TU[]U[N] 時,則為 U

預設建構子

constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
  • 效果

    建構一個空的 shared_ptr

    後置條件

    use_count() == 0 && get() == 0.

指標建構子

template<class Y> explicit shared_ptr(Y * p);
  • 要求

    Y 必須是完整類型。當 T 是陣列類型時,表達式 delete[] p,或當 T 不是陣列類型時,表達式 delete p,必須是格式良好、定義明確且不會擲回例外的。當 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 是無害的。
注意
此建構子是一個樣板,目的是記住傳遞的實際指標類型。解構子將使用相同的指標呼叫 delete,包括其原始類型,即使 T 沒有虛擬解構子,或為 void

接受刪除器的建構子

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

    D 必須是 CopyConstructibleD 的複製建構子和解構子不得擲回例外。表達式 d(p) 必須是格式良好、定義明確且不會擲回例外的。A 必須是 C++ 標準 [allocator.requirements] 章節中所述的 Allocator。當 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 所指向的物件時,會使用儲存的 p 副本作為引數來叫用儲存的 d 副本。
注意
自訂的釋放器允許回傳 shared_ptr 的工廠函式將使用者與其記憶體配置策略隔離。由於釋放器不是型別的一部分,變更配置策略不會破壞原始碼或二進位的相容性,也不需要用戶端重新編譯。例如,當回傳 shared_ptr 到靜態配置的物件時,「無操作」的釋放器很有用,而其他變體則允許將 shared_ptr 用作另一個智慧型指標的包裝器,以簡化互通性。
注意
D 的複製建構子不得擲出例外,此要求來自於傳值呼叫。如果複製建構子擲出例外,指標將會洩漏。

複製和轉換建構子

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

    Y* 應該可以轉換為 T*

    效果

    如果 r 是空的,則建構一個空的 shared_ptr;否則,建構一個與 r 共用所有權的 shared_ptr

    後置條件

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

移動建構子

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

    Y* 應該可以轉換為 T*

    效果

    r 移動建構一個 shared_ptr

    後置條件

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

別名建構子

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

    r 複製建構一個 shared_ptr,同時儲存 p 取代之。

    後置條件

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

別名移動建構子

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

    r 移動建構一個 shared_ptr,同時儲存 p 取代之。

    後置條件

    get() == puse_count() 等於 r 的舊計數。r 是空的且 r.get() == 0

weak_ptr 建構子

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

    Y* 應該可以轉換為 T*

    效果

    建構一個與 r 共用所有權的 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;
  • 回傳

    儲存的指標。

unique

bool unique() const noexcept;
  • 回傳

    use_count() == 1.

use_count

long use_count() const noexcept;
  • 回傳

    *this 共用所有權的 shared_ptr 物件數量,包括 *this,或者當 *this 是空的時候為 0。

轉換

explicit operator bool() const noexcept;
  • 回傳

    get() != 0.

    注意
    此轉換運算子允許在布林上下文中使用 shared_ptr 物件,例如 if(p && p->valid()) {}
注意
轉換為 bool 不僅僅是語法糖。它允許在使用 dynamic_pointer_castweak_ptr::lock 時,在條件中宣告 shared_ptr 變數。
注意
在 C++03 編譯器上,傳回值是不指定的類型。

swap

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

    交換兩個智慧型指標的內容。

owner_before

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

    請參閱 operator< 的描述。

owner_equals

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

    當且僅當 *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<T*>( (U*)0 ) 必須是格式良好的。

    回傳

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

注意
看似等效的表示式 shared_ptr<T>(static_cast<T*>(r.get())) 最終會導致未定義的行為,嘗試兩次刪除同一個物件。

const_pointer_cast

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

    表示式 const_cast<T*>( (U*)0 ) 必須是格式良好的。

    回傳

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

dynamic_pointer_cast

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

    表示式 dynamic_cast<T*>( (U*)0 ) 必須是格式良好的。

    回傳
    • dynamic_cast<typename shared_ptr<T>::element_type*>(r.get()) 回傳非零值 p 時,則為 shared_ptr<T>(r, p)

    • 否則,則為 shared_ptr<T>()

reinterpret_pointer_cast

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

    表示式 reinterpret_cast<T*>( (U*)0 ) 必須是格式良好的。

    回傳

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

operator<<

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

    os << p.get();.

    回傳

    os.

get_deleter

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

    如果 *this 擁有一個類型(非 cv 限定)為 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。該程式會建立 shared_ptr 物件的 std::vectorstd::set

請注意,在容器填滿後,某些 shared_ptr 物件的使用計數將為 1 而不是 2,因為集合是 std::set 而不是 std::multiset,因此不包含重複的項目。此外,在執行 push_backinsert 容器操作時,使用計數在不同的時間可能會更高。更複雜的是,容器操作可能會在各種情況下擲出例外。如果沒有智慧型指標,在本範例中正確地處理記憶體管理和例外處理將是一場惡夢。

Handle/Body 慣用法

shared_ptr 的一個常見用法是實作控制代碼/主體(也稱為 pimpl)慣用語,以避免在標頭檔中公開主體(實作)。

shared_ptr_example2_test.cpp 範例程式包含一個標頭檔 shared_ptr_example2.hpp,該標頭檔使用指向不完整型別的 shared_ptr 來隱藏實作。需要完整型別的成員函式的實例化發生在 shared_ptr_example2.cpp 實作檔案中。請注意,不需要明確的解構子。與 ~scoped_ptr 不同,~shared_ptr 不要求 T 為完整型別。

執行緒安全

shared_ptr 物件提供與內建型別相同的執行緒安全性層級。多個執行緒可以同時「讀取」(僅使用常數操作存取)shared_ptr 實例。不同的 shared_ptr 實例可以由多個執行緒同時「寫入」(使用可變操作(例如 operator=reset)存取)(即使這些實例是副本,並且在底層共用相同的參考計數。)

任何其他同時存取都會導致未定義的行為。

範例

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 的程式庫,您可以在專案範圍內 #define 巨集 BOOST_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(),它在追蹤複雜專案中出現的循環相依性錯誤時非常有用。

  7. 為什麼 shared_ptr 沒有指定複雜度要求?

    因為複雜度要求會限制實作者,並在沒有明顯益處的情況下使 shared_ptr 使用者的規格變得複雜。例如,如果錯誤檢查的實作必須滿足嚴格的複雜度要求,可能會變得不符合規範。

  8. 為什麼 shared_ptr 沒有提供 release() 函式?

    shared_ptr 無法放棄所有權,除非它是 unique(),因為其他副本仍然會銷毀物件。

    請考慮

    shared_ptr<int> a(new int);
    shared_ptr<int> b(a); // a.use_count() == b.use_count() == 2
    
    int * p = a.release();
    
    // Who owns p now? b will still call delete on it in its destructor.

    此外,release() 傳回的指標將難以可靠地解除配置,因為來源 shared_ptr 可能使用自訂的刪除器建立,或者可能指向不同類型的物件。

  9. 為什麼 operator->() 是 const,但其傳回值是指向元素類型的非 const 指標?

    淺層複製指標,包括原始指標,通常不會傳播 constness。這樣做沒有什麼意義,因為您總是能從 const 指標取得非 const 指標,然後透過它修改物件。shared_ptr 盡可能「接近原始指標,但不能更近」。

weak_ptr:非擁有觀察者

描述

weak_ptr 類別樣板儲存對 shared_ptr 已管理的物件的「弱參考」。要存取物件,可以使用接受 weak_ptrshared_ptr 建構函式,或 weak_ptr 成員函式 lock,將 weak_ptr 轉換為 shared_ptr。當指向物件的最後一個 shared_ptr 消失且物件被刪除時,嘗試從引用已刪除物件的 weak_ptr 實例取得 shared_ptr 將會失敗:建構函式會擲回 boost::bad_weak_ptr 類型的例外,而 weak_ptr::lock 將會傳回空的 shared_ptr

每個 weak_ptr 都符合 C++ 標準程式庫的 CopyConstructibleAssignable 要求,因此可以在標準程式庫容器中使用。提供了比較運算子,以便 weak_ptr 可以與標準程式庫的關聯容器一起使用。

weak_ptr 操作永遠不會擲回例外。

類別樣板以 T 作為參數,T 是指向的物件類型。

shared_ptr 相比,weak_ptr 提供的操作子集非常有限,因為在多執行緒程式中存取其儲存的指標通常很危險,有時即使在單一執行緒中也不安全(也就是說,它可能會調用未定義的行為)。假設 weak_ptr 有一個傳回原始指標的 get 成員函式,並考慮這段無害的程式碼

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);

// some time later

if(int * r = q.get())
{
    // use *r
}

想像一下,在 if 之後,但在使用 r 之前,另一個執行緒執行了語句 p.reset()。現在 r 是一個懸空指標。

解決這個問題的方法是從 q 建立一個臨時的 shared_ptr

shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);

// some time later

if(shared_ptr<int> r = q.lock())
{
    // use *r
}

現在 r 持有對 q 所指向物件的參考。即使 p.reset() 在另一個執行緒中執行,物件也會保持存活,直到 r 超出範圍或被重設。透過取得指向物件的 shared_ptr,我們有效地鎖定了它,防止被銷毀。

概要

weak_ptr 定義在 <boost/smart_ptr/weak_ptr.hpp> 中。

namespace boost {

  template<class T> class weak_ptr {
  public:

    typedef /*see below*/ element_type;

    weak_ptr() noexcept;

    template<class Y> weak_ptr(shared_ptr<Y> const & r) noexcept;
    weak_ptr(weak_ptr const & r) noexcept;
    template<class Y> weak_ptr(weak_ptr<Y> const & r) noexcept;

    weak_ptr(weak_ptr && r) noexcept;

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

    ~weak_ptr() noexcept;

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

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

    bool empty() const noexcept;

    shared_ptr<T> lock() const noexcept;

    void reset() noexcept;

    void swap(weak_ptr<T> & b) noexcept;

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

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

    std::size_t owner_hash_value() const noexcept;
  };

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

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

成員

element_type

typedef ... element_type;

T 不是陣列類型時,element_typeT,當 TU[]U[N] 時,則為 U

建構子

weak_ptr() noexcept;
  • 效果

    建構空的 weak_ptr

    後置條件

    use_count() == 0.

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

    如果 r 為空,則建構空的 weak_ptr;否則,建構一個與 r 共用所有權的 weak_ptr,如同儲存 r 中儲存的指標的副本一樣。

    後置條件

    use_count() == r.use_count().

weak_ptr(weak_ptr && r) noexcept;
  • 效果

    建構一個具有 r 持有值的 weak_ptr

    後置條件

    r 為空。

別名建構函式

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

r 建構 weak_ptr,如同使用對應的轉換/複製/移動建構函式,但儲存 p 來代替。

後置條件

use_count() == r.use_count()。當 !expired() 時,shared_ptr<T>(*this).get() == p

注意
這些建構函式是擴充功能,在 std::weak_ptr 中不存在。

解構子

~weak_ptr() noexcept;
  • 效果

    銷毀此 weak_ptr,但對其儲存指標指向的物件沒有影響。

賦值

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

    等同於 weak_ptr(r).swap(*this)

    注意
    實作可以透過不同的方式滿足效果(和隱含的保證),而無需建立臨時變數。

use_count

long use_count() const noexcept;
  • 回傳

    如果 *this 為空,則為 0;否則,為與 *this 共用所有權的 shared_ptr 物件的數量。

過期的

bool expired() const noexcept;
  • 回傳

    use_count() == 0.

空的

bool empty() const noexcept;
  • 回傳

    *this 為空時為 true,否則為 false

    注意
    此函式是擴充功能,在 std::weak_ptr 中不存在。

鎖定

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

    expired()? shared_ptr<T>(): shared_ptr<T>(*this).

reset

void reset() noexcept;
  • 效果

    等同於 weak_ptr().swap(*this)

swap

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

    交換兩個智慧型指標的內容。

owner_before

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

    請參閱 operator< 的描述。

owner_equals

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

    當且僅當 *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 嗎?

    不行。weak_ptr 只能從 shared_ptr 建立,而且在物件建構時,還不存在指向該物件的 shared_ptr。即使您可以建立一個指向 this 的臨時 shared_ptr,它也會在建構函式結束時超出範圍,而所有 weak_ptr 實例都會立即過期。

    解決方案是將建構函式設為私有,並提供一個傳回 shared_ptr 的工廠函式

    class X
    {
    private:
    
        X();
    
    public:
    
        static shared_ptr<X> create()
        {
            shared_ptr<X> px(new X);
            // create weak pointers from px here
            return px;
        }
    };

make_shared:建立 shared_ptr

描述

函式樣板 make_sharedallocate_shared 提供了方便、安全且有效率的方式來建立 shared_ptr 物件。

原理

一致地使用 shared_ptr 可以消除使用顯式 delete 的需要,但它本身並不能支援避免顯式 new。使用者不斷要求提供一個工廠函式,該函式會建立給定類型的物件並傳回指向該物件的 shared_ptr。除了方便和風格之外,這樣的功能也具有例外安全性和顯著的速度優勢,因為它可以針對物件及其對應的控制區塊使用單一配置,從而消除 shared_ptr 建構開銷的重要部分。這消除了關於 shared_ptr 的主要效率抱怨之一。

提供了一系列的超載函式樣板 make_sharedallocate_shared 來解決此需求。make_shared 使用全域 operator new 來配置記憶體,而 allocate_shared 使用使用者提供的配置器,從而可以進行更精細的控制。

選擇名稱 make_shared 的理由是,表達式 make_shared<Widget>() 可以大聲朗讀,並傳達預期的含義。

最初,Boost 函式樣板 allocate_sharedmake_shared 僅適用於純量物件。需要有效率地配置陣列物件。對類別樣板 shared_array 的一個批評一直是缺乏像 make_shared 這樣僅使用單一配置的公用程式。當 shared_ptr 增強為支援陣列類型時,也為陣列類型提供了 allocate_sharedmake_shared 的其他超載。

概要

make_sharedallocate_shared 定義在 <boost/smart_ptr/make_shared.hpp> 中。

namespace boost {
  // T is not an array
  template<class T, class... Args>
    shared_ptr<T> make_shared(Args&&... args);
  template<class T, class A, class... Args>
    shared_ptr<T> allocate_shared(const A& a, Args&&... args);

  // T is an array of unknown bounds
  template<class T>
    shared_ptr<T> make_shared(std::size_t n);
  template<class T, class A>
    shared_ptr<T> allocate_shared(const A& a, std::size_t n);

  // T is an array of known bounds
  template<class T>
    shared_ptr<T> make_shared();
  template<class T, class A>
    shared_ptr<T> allocate_shared(const A& a);

  // T is an array of unknown bounds
  template<class T> shared_ptr<T>
    make_shared(std::size_t n, const remove_extent_t<T>& v);
  template<class T, class A> shared_ptr<T>
    allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);

  // T is an array of known bounds
  template<class T>
    shared_ptr<T> make_shared(const remove_extent_t<T>& v);
  template<class T, class A>
    shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);

  // T is not an array of unknown bounds
  template<class T>
    shared_ptr<T> make_shared_noinit();
  template<class T, class A>
    shared_ptr<T> allocate_shared_noinit(const A& a);

  // T is an array of unknown bounds
  template<class T>
    shared_ptr<T> make_shared_noinit(std::size_t n);
  template<class T, class A>
    shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n);
}

通用要求

除非另有說明,否則適用於所有 make_sharedallocate_shared 超載的通用要求如下所述。

要求

A 應為配置器A 的複製建構函式和解構函式不得擲回例外。

效果

配置類型為 T 的物件或 n 個類型為 U 的物件的記憶體(如果 T 是形式為 U[] 的陣列類型,並且 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) 執行此初始化(其中 exprvstd::forward<Args>(args)...),而 p 的類型為 void*,並指向適合儲存類型為 U 的物件的儲存體。

  • 當指定將非陣列類型 U 的(子)物件初始化為值 v,或從 args... 建構時,allocate_shared 應透過表達式 std::allocator_traits<A2>::construct(a2, p, expr) 執行此初始化(其中 exprvstd::forward<Args>(args)...)p 指向適合儲存類型為 U 的物件的儲存體,而類型為 A2a2a 的潛在重新繫結副本。

  • 當指定將非陣列類型 U 的(子)物件進行預設初始化時,make_shared_noinitallocate_shared_noinit 應透過表達式 ::new(p) U 執行此初始化,其中 p 的類型為 void*,並指向適合儲存類型為 U 的物件的儲存體。

  • 當指定將非陣列類型 U 的(子)物件進行值初始化時,make_shared 應透過表達式 ::new(p) U() 執行此初始化,其中 p 的類型為 void*,並指向適合儲存類型為 U 的物件的儲存體。

  • 當指定將非陣列類型 U 的(子)物件進行值初始化時,allocate_shared 應透過表達式 std::allocator_traits<A2>::construct(a2, p) 執行此初始化,其中 p 指向適合儲存類型為 U 的物件的儲存體,而類型為 A2a2a 的潛在重新繫結副本。

  • 陣列元素會依照其位址的升序進行初始化。

  • 當傳回值所管理的物件的生命週期結束時,或當陣列元素的初始化擲回例外時,已初始化的元素應依其建構的相反順序銷毀。

注意
這些函式通常會配置比元素物件總大小更多的記憶體,以容納內部簿記結構,例如參考計數。

自由函式

template<class T, class... Args>
  shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
  shared_ptr<T> allocate_shared(const A& a, Args&&... args);
  • 約束

    T 不是陣列。

    回傳

    指向類型為 T 的物件的 shared_ptr,從 args... 建構。

    範例
  • auto p = make_shared<int>();

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

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

    T 是未知邊界的陣列。

    回傳

    指向 n 個類型為 remove_extent_t<T> 的值初始化物件序列的 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 是已知邊界的陣列。

    回傳

    指向 extent_v<T> 個類型為 remove_extent_t<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 是未知邊界的陣列。

    回傳

    指向 n 個類型為 remove_extent_t<T> 的物件序列的 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 的成員函式,它們會根據常數性,回傳指向 thisshared_ptr<T>shared_ptr<T const>。它也定義了兩個名為 weak_from_this 的成員函式,它們會回傳對應的 weak_ptr

範例

#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>

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

    boost::shared_ptr<Y> f()
    {
        return shared_from_this();
    }
};

int main()
{
    boost::shared_ptr<Y> p(new Y);
    boost::shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

概要

enable_shared_from_this 定義在 <boost/smart_ptr/enable_shared_from_this.hpp> 中。

namespace boost {

  template<class T> class enable_shared_from_this {
  private:

    // exposition only
    weak_ptr<T> weak_this_;

  protected:

    enable_shared_from_this() = default;
    ~enable_shared_from_this() = default;

    enable_shared_from_this(const enable_shared_from_this&) noexcept;
    enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept;

  public:

    shared_ptr<T> shared_from_this();
    shared_ptr<T const> shared_from_this() const;

    weak_ptr<T> weak_from_this() noexcept;
    weak_ptr<T const> weak_from_this() const noexcept;
  }
}

成員

enable_shared_from_this(enable_shared_from_this const &) noexcept;
  • 效果

    預設建構 weak_this_

    注意
    weak_this_ *不會* 從引數複製。
enable_shared_from_this& operator=(enable_shared_from_this const &) noexcept;
  • 回傳

    *this.

    注意
    weak_this_ 不會變更。
template<class T> shared_ptr<T> shared_from_this();
template<class T> shared_ptr<T const> shared_from_this() const;
  • 回傳

    shared_ptr<T>(weak_this_).

    注意
    *this 不被 shared_ptr 擁有時,這些成員會拋出 bad_weak_ptr
注意

weak_this_ 被一個指向 *this 的指標建構時,它會被 shared_ptr 初始化為自身的副本。例如,在以下程式碼中

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

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

p 的建構會自動將 p->weak_this_ 初始化為 p

template<class T> weak_ptr<T> weak_from_this() noexcept;
template<class T> weak_ptr<T const> weak_from_this() const noexcept;
  • 回傳

    weak_this_.

    注意
    shared_from_this() 不同,weak_from_this() 在解構子中是有效的,並且會回傳一個 expired()weak_ptr,但它仍然與其他參考該物件的 weak_ptr 實例(如果有的話)共享所有權。

enable_shared_from

描述

enable_shared_from 作為基底類別使用,允許藉由使用 shared_fromweak_from 函式,取得指向物件的原始指標的 shared_ptrweak_ptr

enable_shared_fromenable_shared_from_this<T> 的不同之處在於它不是樣板,並且是建議用於新程式碼的替代方案。

範例

#include <boost/smart_ptr/enable_shared_from.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>

class Y: public boost::enable_shared_from
{
public:

    boost::shared_ptr<Y> f()
    {
        return boost::shared_from( this );
    }
};

int main()
{
    boost::shared_ptr<Y> p(new Y);
    boost::shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

概要

enable_shared_from 定義在 <boost/smart_ptr/enable_shared_from.hpp> 中。

namespace boost {

  class enable_shared_from: public enable_shared_from_this<enable_shared_from>
  {
  };

  template<class T> shared_ptr<T> shared_from( T * p );
  template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
}

函式

template<class T> shared_ptr<T> shared_from( T * p );
  • 回傳

    shared_ptr<T>( p->enable_shared_from::shared_from_this(), p ).

    注意
    p 不被 shared_ptr 擁有時,會拋出 bad_weak_ptr
template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
  • 回傳

    weak_ptr<T>( p->enable_shared_from::weak_from_this(), p ).

    注意
    shared_from(this) 不同,weak_from(this) 在解構子中是有效的,並且會回傳一個 expired()weak_ptr,但它仍然與其他參考該物件的 weak_ptr 實例(如果有的話)共享所有權。

make_unique:建立 unique_ptr

描述

make_unique 函式樣板提供方便且安全的方式來建立 std::unique_ptr 物件。

原理

C++11 標準引入了 std::unique_ptr,但沒有提供任何像 std::make_shared 一樣提供相同異常安全性與避免撰寫 new 運算式的 make_unique 工具。在某些標準程式庫廠商實作它之前(以及在 C++14 標準引入 std::make_unique 之前),此程式庫由於使用者的要求而提供了它。

當使用者不需要或不想承擔數值初始化的開銷時,此程式庫也提供了 make_unique 的額外多載以進行預設初始化。C++20 標準現在透過 std::make_unique_for_overwrite 提供此功能。

概要

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

namespace boost {
  // 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 標準引入了使用運算子 new 來建立新物件的 std::make_unique。然而,在標準程式庫中,沒有方便的工具可以使用分配器來建立由 std::unique_ptr 管理的物件。撰寫分配器感知程式碼的使用者經常要求 allocate_unique 工廠函式。此函式對於 std::unique_ptr 就像 std::allocate_shared 對於 std::shared_ptr 一樣。

概要

allocate_unique 定義在 <boost/smart_ptr/allocate_unique.hpp> 中。

namespace boost {
  template<class T, class A>
  class alloc_deleter;

  template<class T, class A>
  using alloc_noinit_deleter = alloc_deleter<T, noinit_adaptor<A>>;

  // T is not an array
  template<class T, class A, class... Args>
    std::unique_ptr<T, alloc_deleter<T, A>>
      allocate_unique(const A& a, Args&&... args);

  // T is not an array
  template<class T, class A>
    std::unique_ptr<T, alloc_deleter<T, A>>
      allocate_unique(const A& a, type_identity_t<T>&& v);

  // T is an array of unknown bounds
  template<class T, class A>
    std::unique_ptr<T, alloc_deleter<T, A>>
      allocate_unique(const A& a, std::size_t n);

  // T is an array of known bounds
  template<class T, class A>
    std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
      allocate_unique(const A& a);

  // T is an array of unknown bounds
  template<class T, class A>
    std::unique_ptr<T, alloc_deleter<T, A>>
      allocate_unique(const A& a, std::size_t n, const remove_extent_t<T>& v);

  // T is an array of known bounds
  template<class T, class A>
    std::unique_ptr<remove_extent_t<T>[], alloc_deleter<T, A>>
      allocate_unique(const A& a, const remove_extent_t<T>& v);

  // T is not an array
  template<class T, class A>
    std::unique_ptr<T, alloc_noinit_deleter<T, A>>
      allocate_unique_noinit(const A& a);

  // T is an array of unknown bounds
  template<class T, class A>
    std::unique_ptr<T, alloc_noinit_deleter<T, A>>
      allocate_unique_noinit(const A& a, std::size_t n);

  // T is an array of known bounds
  template<class T, class A>
    std::unique_ptr<remove_extent_t<T>[], alloc_noinit_deleter<T, A>>
      allocate_unique_noinit(const A& a);

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

通用要求

除非另有說明,否則適用於所有 allocate_uniqueallocate_unique_noinit 多載的常見需求如下所述。

要求

A 應為配置器A 的複製建構函式和解構函式不得擲回例外。

效果

配置類型為 T 的物件或 n 個類型為 U 的物件的記憶體(如果 T 是形式為 U[] 的陣列類型,並且 n 由引數決定,如具體超載所指定)。物件是從引數初始化的,如具體超載所指定。使用 a 的重新繫結副本(對於未指定的 value_type)來配置記憶體。如果擲回例外,則函式無效。

回傳

一個 std::unique_ptr 實例,它儲存並擁有新建構物件的位址。

後置條件

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

擲回

A::allocate 或從物件初始化拋出的例外。

備註
  • 當指定將陣列類型的物件初始化為相同類型的值 v 時,這應解釋為表示該物件的每個陣列元素都會初始化為 v 中的對應元素。

  • 當指定陣列類型的物件要進行值初始化時,這應解釋為表示該物件的每個陣列元素都會進行值初始化。

  • 當指定將非陣列型別 U 的(子)物件初始化為值 v,或從 args... 建構時,allocate_unique 應透過運算式 std::allocator_traits<A2>::construct(a2, p, expr) 執行此初始化(其中 expr 分別為 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 不是陣列。

    回傳

    指向型別為 T 的物件的 std::unique_ptr,從 args... 建構。

    範例
  • 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 不是陣列。

    回傳

    指向型別為 T 的物件的 std::unique_ptr,從 v 建構。

    範例

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

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

    T 是未知邊界的陣列。

    回傳

    指向 n 個數值初始化的型別為 remove_extent_t<T> 的物件序列的 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 是已知邊界的陣列。

    回傳

    指向 extent_v<T> 個數值初始化的型別為 remove_extent_t<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 是未知邊界的陣列。

    回傳

    指向 n 個型別為 remove_extent_t<T> 的物件序列的 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 是已知邊界的陣列。

    回傳

    指向 extent_v<T> 個型別為 remove_extent_t<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 是未知邊界的陣列。

    回傳

    指向 n 個預設初始化的型別為 remove_extent_t<T> 的物件序列的 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 是已知邊界的陣列。

    回傳

    指向 extent_v<T> 個預設初始化的型別為 remove_extent_t<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 是所指向物件的型別。只要 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 會建立新的本地參考計數;這表示從同一個 shared_ptr 建立的兩個 local_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 會導致兩者共用相同的計數

程式碼範例 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] 時,則為 U

預設建構子

constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
  • 效果

    建構空的 local_shared_ptr

    後置條件

    local_use_count() == 0 && get() == 0.

指標建構子

template<class Y> explicit local_shared_ptr(Y * p);
  • 效果

    建構擁有 shared_ptr<T>( p )local_shared_ptr

    後置條件

    local_use_count() == 1 && get() == p.

    擲回

    std::bad_alloc,或當無法取得記憶體以外的資源時,實作定義的例外。

接受刪除器的建構子

template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
  • 效果

    建構擁有 shared_ptr<T>( p, d )local_shared_ptr

    後置條件

    local_use_count() == 1 && get() == p.

    擲回

    std::bad_alloc,或當無法取得記憶體以外的資源時,實作定義的例外。

template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
  • 效果

    建構擁有 shared_ptr<T>( p, d, a )local_shared_ptr

    後置條件

    local_use_count() == 1 && get() == p.

    擲回

    std::bad_alloc,或當無法取得記憶體以外的資源時,實作定義的例外。

複製和轉換建構子

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

    Y* 應該可以轉換為 T*

    效果

    如果 r 為空,則建構空的 local_shared_ptr;否則,建構與 r 共用所有權的 local_shared_ptr

    後置條件

    get() == r.get() && local_use_count() == r.local_use_count().

移動建構子

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

    Y* 應該可以轉換為 T*

    效果

    r 移動建構 local_shared_ptr

    後置條件

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

shared_ptr 建構子

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

    建構擁有 rlocal_shared_ptr

    後置條件

    local_use_count() == 1get() 傳回 r.get() 的舊值。

    擲回

    std::bad_alloc,或當無法取得記憶體以外的資源時,實作定義的例外。

別名建構子

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

    建構與 r 共用所有權並儲存 plocal_shared_ptr

    後置條件

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

別名移動建構子

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

    r 移動建構 local_shared_ptr,同時改為儲存 p

    後置條件

    get() == plocal_use_count() 等於 r 的舊計數。r 為空且 r.get() == 0

unique_ptr 建構子

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

    Y* 應該可以轉換為 T*

    效果
    • r.get() == 0 時,等同於 local_shared_ptr()

    • 否則,建構擁有 shared_ptr<T>( std::move(r) )local_shared_ptr

    擲回

    std::bad_alloc,或當無法取得記憶體以外的資源時,實作定義的例外。

    異常安全

    如果擲出例外,則建構子不會有任何作用。

解構子

~local_shared_ptr() noexcept;
  • 效果
    • 如果 *this 為空,或與另一個 local_shared_ptr 實例共用所有權(local_use_count() > 1),則不會有任何副作用。

    • 否則,銷毀擁有的 shared_ptr

賦值

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

    等同於 local_shared_ptr(r).swap(*this)

    回傳

    *this.

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

    等同於 local_shared_ptr(std::move(r)).swap(*this)

    回傳

    *this.

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

    等同於 local_shared_ptr().swap(*this)

    回傳

    *this.

reset

void reset() noexcept;
  • 效果

    等同於 local_shared_ptr().swap(*this)

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

    等同於 local_shared_ptr(p).swap(*this)

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

    等同於 local_shared_ptr(p, d).swap(*this)

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

    等同於 local_shared_ptr(p, d, a).swap(*this)

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

    等同於 local_shared_ptr(r, p).swap(*this)

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

    等同於 local_shared_ptr(std::move(r), p).swap(*this)

間接

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

    T 不應為陣列類型。

    回傳

    *get().

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

    T 不應為陣列類型。

    回傳

    get().

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

    T 應該是陣列型別。儲存的指標不得為 0。i >= 0。如果 TU[N],則 i < N

    回傳

    get()[i].

get

element_type * get() const noexcept;
  • 回傳

    儲存的指標。

local_use_count

long local_use_count() const noexcept;
  • 回傳

    local_shared_ptr 物件的數量,包括 *this,與 *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<T*>( (U*)0 ) 必須是格式良好的。

    回傳

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

注意
表面上等效的表示式 local_shared_ptr<T>(static_cast<T*>(r.get())) 最終會導致未定義行為,嘗試兩次刪除同一個物件。

const_pointer_cast

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

    表示式 const_cast<T*>( (U*)0 ) 必須是格式良好的。

    回傳

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

dynamic_pointer_cast

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

    表示式 dynamic_cast<T*>( (U*)0 ) 必須是格式良好的。

    回傳
    • dynamic_cast<typename local_shared_ptr<T>::element_type*>(r.get()) 傳回非零值 p 時,local_shared_ptr<T>(r, p)

    • 否則,local_shared_ptr<T>()

reinterpret_pointer_cast

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

    表示式 reinterpret_cast<T*>( (U*)0 ) 必須是格式良好的。

    回傳

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

operator<<

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

    os << p.get();.

    回傳

    os.

get_deleter

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

    如果 *this 擁有 shared_ptr 實例 p,則為 get_deleter<D>( p ),否則為 0。

make_local_shared:建立 local_shared_ptr

描述

函式樣板 make_local_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 一起使用,但沒有必要,因為 weak_ptrboost::hashstd::hash 已經使用基於所有權的相等性。)

範例

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,以及當 X 不完整時,shared_ptr<X> 可以複製和銷毀的事實。

「Pimpl」慣用法

不完整類別模式的 C++ 特定變體是「Pimpl」慣用語。不完整類別不會暴露給使用者;它隱藏在轉發外觀的後面。shared_ptr 可用於實作「Pimpl」

// file.hpp:

class file
{
private:

    class impl;
    shared_ptr<impl> pimpl_;

public:

    file(char const * name, char const * mode);

    // compiler generated members are fine and useful

    void read(void * data, size_t size);
};

// file.cpp:

#include "file.hpp"

class file::impl
{
private:

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

    // private data

public:

    impl(char const * name, char const * mode) { ... }
    ~impl() { ... }
    void read(void * data, size_t size) { ... }
};

file::file(char const * name, char const * mode): pimpl_(new impl(name, mode))
{
}

void file::read(void * data, size_t size)
{
    pimpl_->read(data, size);
}

這裡需要注意的關鍵是編譯器產生的複製建構子、指派運算子和解構子都具有合理的含義。因此,fileCopyConstructibleAssignable,允許在標準容器中使用。

使用抽象類別來隱藏實作

另一種廣泛使用的 C++ 慣用語用於分離介面和實作是使用抽象基底類別和工廠函式。抽象類別有時稱為「介面」,而此模式稱為「基於介面的程式設計」。同樣,shared_ptr 可以用作工廠函式的傳回型別

// X.hpp:

class X
{
public:

    virtual void f() = 0;
    virtual void g() = 0;

protected:

    ~X() {}
};

shared_ptr<X> createX();

// X.cpp:

class X_impl: public X
{
private:

    X_impl(X_impl const &);
    X_impl & operator=(X_impl const &);

public:

    virtual void f()
    {
      // ...
    }

    virtual void g()
    {
      // ...
    }
};

shared_ptr<X> createX()
{
    shared_ptr<X> px(new X_impl);
    return px;
}

shared_ptr 的一個關鍵屬性是配置、建構、解除配置和銷毀詳細資訊是在建構時(在工廠函式內部)擷取的。

請注意上面範例中的受保護和非虛擬解構子。用戶端程式碼不能也不需要刪除指向 X 的指標;從 createX 傳回的 shared_ptr<X> 實例將正確呼叫 ~X_impl

防止 delete px.get()

通常希望防止用戶端程式碼刪除由 shared_ptr 管理的指標。先前的技術展示了一種可能的方法,即使用受保護的解構子。另一種替代方法是使用私有刪除器

class X
{
private:

    ~X();

    class deleter;
    friend class deleter;

    class deleter
    {
    public:

        void operator()(X * p) { delete p; }
    };

public:

    static shared_ptr<X> create()
    {
        shared_ptr<X> px(new X, X::deleter());
        return px;
    }
};

封裝配置詳細資料,包裝工廠函式

shared_ptr 可用於建立 C++ 包裝函式,以封裝從工廠函式傳回原始指標的現有 C 樣式程式庫介面,以封裝配置詳細資訊。例如,考慮以下介面,其中 CreateX 可能會從其自己的私有堆積配置 X~X 可能無法存取,或者 X 可能不完整

X * CreateX();
void DestroyX(X *);

可靠銷毀 CreateX 傳回的指標的唯一方法是呼叫 DestroyX

以下是基於 shared_ptr 的包裝函式的樣子

shared_ptr<X> createX()
{
    shared_ptr<X> px(CreateX(), DestroyX);
    return px;
}

呼叫 createX 的用戶端程式碼仍然不需要知道物件是如何配置的,但現在銷毀是自動的。

使用 shared_ptr 來保存靜態配置物件的指標

有時希望建立指向現有物件的 shared_ptr,以便當不再有任何參考時,shared_ptr 不會嘗試銷毀該物件。例如,工廠函式

shared_ptr<X> createX();

在某些情況下可能需要傳回指向靜態配置的 X 實例的指標。

解決方案是使用什麼都不做的自訂刪除器

struct null_deleter
{
    void operator()(void const *) const
    {
    }
};

static X x;

shared_ptr<X> createX()
{
    shared_ptr<X> px(&x, null_deleter());
    return px;
}

相同的技術適用於任何已知比指標壽命更長的物件。

使用 shared_ptr 來保存 COM 物件的指標

背景:COM 物件具有內嵌的參考計數和兩個操作它的成員函式。AddRef() 會遞增計數。當計數降至零時,Release() 會遞減計數並自行銷毀。

可以將指向 COM 物件的指標保留在 shared_ptr

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

但是,請注意從 pw 建立的 shared_ptr 副本不會在 COM 物件的內嵌計數中「註冊」;它們將共用在 make_shared_from_COM 中建立的單個參考。當最後一個 shared_ptr 被銷毀時,從 pw 建立的弱指標將失效,而無論 COM 物件本身是否仍然存活。

如同在 mem_fn 文件中 解釋 的,您需要先 #define BOOST_MEM_FN_ENABLE_STDCALL

使用 shared_ptr 來保存具有內嵌參考計數的物件指標

這是上述技術的推廣。該範例假設物件實作了 intrusive_ptr 所需的兩個函式 intrusive_ptr_add_refintrusive_ptr_release

template<class T> struct intrusive_deleter
{
    void operator()(T * p)
    {
        if(p) intrusive_ptr_release(p);
    }
};

shared_ptr<X> make_shared_from_intrusive(X * p)
{
    if(p) intrusive_ptr_add_ref(p);
    shared_ptr<X> px(p, intrusive_deleter<X>());
    return px;
}

使用 shared_ptr 來保存另一個共享擁有權智慧指標

shared_ptr 的設計目標之一是在程式庫介面中使用。可能會遇到程式庫採用 shared_ptr 引數的情況,但手頭的物件正在由不同的參考計數或連結智慧指標管理。

可以利用 shared_ptr 的自訂刪除器功能,將此現有的智慧指標包裝在 shared_ptr 外觀後面

template<class P> struct smart_pointer_deleter
{
private:

    P p_;

public:

    smart_pointer_deleter(P const & p): p_(p)
    {
    }

    void operator()(void const *)
    {
        p_.reset();
    }

    P const & get() const
    {
        return p_;
    }
};

shared_ptr<X> make_shared_from_another(another_ptr<X> qx)
{
    shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx));
    return px;
}

一個微妙的點是刪除器不允許擲回例外,並且上述範例假設 p_.reset() 不會擲回。如果不是這種情況,則應將 p_.reset(); 包裝在忽略例外的 try {} catch(…​) {} 區塊中。在擲回並忽略例外(通常不太可能)的情況下,當刪除器的生命週期結束時,p_ 將被釋放。當所有參考(包括弱指標)被銷毀或重設時,就會發生這種情況。

另一個曲折是,給定上述 shared_ptr 實例,可以使用 get_deleter 恢復原始的智慧指標

void extract_another_from_shared(shared_ptr<X> px)
{
    typedef smart_pointer_deleter< another_ptr<X> > deleter;

    if(deleter const * pd = get_deleter<deleter>(px))
    {
        another_ptr<X> qx = pd->get();
    }
    else
    {
        // not one of ours
    }
}

從原始指標取得 shared_ptr

有時需要獲取 shared_ptr 給定一個原始指標,該指標指向另一個 shared_ptr 實例已經管理的物件。範例

void f(X * p)
{
    shared_ptr<X> px(???);
}

f 內部,我們想要建立指向 *pshared_ptr

在一般情況下,此問題無解。一種方法是修改 f 以接受 shared_ptr(如果可能)

void f(shared_ptr<X> px);

相同的轉換可以用於非虛擬成員函式,以轉換隱含的 this

void X::f(int m);

將變成具有 shared_ptr 第一個引數的自由函式

void f(shared_ptr<X> this_, int m);

如果無法變更 f,但 X 使用內嵌計數,則使用上述 make_shared_from_intrusive。或者,如果已知在 f 中建立的 shared_ptr 永遠不會比物件壽命更長,則使用空刪除器

在建構子中取得指向此物件的 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;
    }
};

取得指向此物件的 shared_ptr

有時需要在虛擬成員函式中從 this 取得 shared_ptr,並假設 this 已經由 shared_ptr 管理。無法套用先前技術中描述的轉換

一個典型的範例

class X
{
public:

    virtual void f() = 0;

protected:

    ~X() {}
};

class Y
{
public:

    virtual shared_ptr<X> getX() = 0;

protected:

    ~Y() {}
};

// --

class impl: public X, public Y
{
public:

    impl() { ... }

    virtual void f() { ... }

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

解決方案是將指向 this 的弱指標作為 impl 的成員保留

class impl: public X, public Y
{
private:

    weak_ptr<impl> weak_this;

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

    impl() { ... }

public:

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

    virtual void f() { ... }

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

此函式庫現在包含一個輔助類別模板 enable_shared_from_this,可用於封裝解決方案。

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

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

public:

    virtual void f() { ... }

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

請注意,您不再需要在 enable_shared_from_this 中手動初始化 weak_ptr 成員。建構 implshared_ptr 會處理此問題。

使用 shared_ptr 作為智慧計數句柄

某些函式庫介面使用不透明的句柄,這是上述 不完整類別技術 的一種變體。例如:

typedef void * HANDLE;

HANDLE CreateProcess();
void CloseHandle(HANDLE);

除了原始指標,也可以使用 shared_ptr 作為句柄,並免費獲得參考計數和自動資源管理。

typedef shared_ptr<void> handle;

handle createProcess()
{
    shared_ptr<void> pv(CreateProcess(), CloseHandle);
    return pv;
}

使用 shared_ptr 在區塊結束時執行程式碼

當控制流程離開作用域時,shared_ptr<void> 可以自動執行清理程式碼。

  • 執行 f(p),其中 p 是一個指標。

    shared_ptr<void> guard(p, f);
  • 執行任意程式碼:f(x, y)

    shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y));

使用 shared_ptr<void> 來保存任意物件

shared_ptr<void> 可以作為類似 void* 的通用物件指標。當 shared_ptr<void> 實例建構為:

shared_ptr<void> pv(new X);

被銷毀時,它會正確地執行 ~X 來處置 X 物件。

此特性可以用與原始 void* 相同的方式來暫時從物件指標中移除型別資訊。之後可以使用 static_pointer_castshared_ptr<void> 轉換回正確的型別。

將任意資料與異質的 shared_ptr 實例關聯

shared_ptrweak_ptr 支援標準關聯容器(如 std::map)所需的 operator< 比較。這可以用於以非侵入式方式將任意資料與 shared_ptr 管理的物件相關聯。

typedef int Data;

std::map<shared_ptr<void>, Data> userData;
// or std::map<weak_ptr<void>, Data> userData; to not affect the lifetime

shared_ptr<X> px(new X);
shared_ptr<int> pi(new int(3));

userData[px] = 42;
userData[pi] = 91;

使用 shared_ptr 作為 CopyConstructible 互斥鎖

有時,有必要從函式返回互斥鎖,而不可複製的鎖不能以傳值方式返回。可以使用 shared_ptr 作為互斥鎖。

class mutex
{
public:

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

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

更好的是,作為鎖的 shared_ptr 實例可以封裝在專用的 shared_lock 類別中。

class shared_lock
{
private:

    shared_ptr<void> pv;

public:

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

現在可以將 shared_lock 用作:

shared_lock lock(m);

請注意,由於 shared_ptr<void> 隱藏型別資訊的能力,shared_lock 並不以互斥鎖型別作為模板參數。

使用 shared_ptr 來包裝成員函式呼叫

shared_ptr 實現了 Bjarne Stroustrup 在文章「包裝 C++ 成員函式呼叫」(可在線上 http://www.stroustrup.com/wrapper.pdf 找到)中描述的 Wrap/CallProxy 方案所需的擁有權語義。以下是一個實作範例:

template<class T> class pointer
{
private:

    T * p_;

public:

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

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

class X
{
private:

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

public:

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

int main()
{
    X x;

    pointer<X> px(&x);

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

延遲釋放

在某些情況下,單一的 px.reset() 會在效能攸關的區域觸發昂貴的釋放記憶體操作。

class X; // ~X is expensive

class Y
{
    shared_ptr<X> px;

public:

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

解決方法是將 px 移動到專用的可用清單中,以延遲潛在的釋放記憶體操作,該列表可以在效能和回應時間不是問題時定期清空。

vector< shared_ptr<void> > free_list;

class Y
{
    shared_ptr<X> px;

public:

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

// periodically invoke free_list.clear() when convenient

另一種變化是通過使用延遲刪除器將可用清單邏輯移動到建構點。

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

指向非 shared_ptr 管理之物件的弱指標

使用 null_deleter 讓物件持有指向自身的 shared_ptr

class X
{
private:

    shared_ptr<X> this_;
    int i_;

public:

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

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

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

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

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

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

當物件的生命週期結束時,X::this_ 將被銷毀,並且所有弱指標將自動過期。

附錄 B:歷史和致謝

1994 年夏季

Greg Colvin 向 C++ 標準委員會 提議了名為 auto_ptrcounted_ptr 的類別,它們非常類似於我們現在稱之為 scoped_ptrshared_ptr 的類別。在少數幾個圖書館工作組的建議沒有被全委員會採納的情況下,counted_ptr 被拒絕,並且令人驚訝的擁有權轉移語義被添加到 auto_ptr

1998 年 10 月

Beman Dawes 提議以 safe_ptrcounted_ptr 的名稱恢復原始語義,並與 Per Andersson、Matt Austern、Greg Colvin、Sean Corfield、Pete Becker、Nico Josuttis、Dietmar Kühl、Nathan Myers、Chichiang Wan 和 Judy Ward 會面。在討論過程中,確定了四個新的類別名稱,決定沒有必要完全遵循 std::auto_ptr 介面,並確定了各種函式簽名和語義。

在接下來的三個月中,考慮了 shared_ptr 的幾種實作方式,並在 boost.org 郵件列表中進行了討論。實作問題圍繞著必須保留的參考計數,可以附加到指向的物件,也可以在其他地方分離。這些變體本身都有兩種主要的變體:

  • 直接分離:shared_ptr 包含一個指向物件的指標和一個指向計數的指標。

  • 間接分離:shared_ptr 包含一個指向輔助物件的指標,該輔助物件又包含一個指向物件和計數的指標。

  • 嵌入式附加:計數是指向物件的成員。

  • 位置附加:計數是通過運算符 new 操作附加的。

每種實作技術都有優缺點。我們甚至對直接和間接方法進行了各種計時,發現至少在 Intel Pentium 晶片上,幾乎沒有可測量的差異。Kevlin Henney 提供了一篇他寫的關於「計數主體技術」的論文。Dietmar Kühl 提出了一種優雅的部分範本特殊化技術,允許使用者選擇他們喜歡的實作方式,並且也進行了實驗。

但 Greg Colvin 和 Jerry Schwarz 認為「參數化會讓使用者望而卻步」,最終我們選擇只提供直接實作。

1999 年 5 月

在 1999 年的 4 月和 5 月,Valentin Bonnard 和 David Abrahams 提出了許多建議,從而實現了許多改進。

1999 年 9 月

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

1999 年 11 月

Darin Adler 提供了共享型別的 operator ==operator != 以及 std::swapstd::less 特殊化。

2001 年 5 月

Vladimir Prus 建議在銷毀時需要完整的型別。改進是在 Dave Abrahams、Greg Colvin、Beman Dawes、Rainer Deyke、Peter Dimov、John Maddock、Vladimir Prus、Shankar Sai 等人的討論中發展起來的。

2002 年 1 月

Peter Dimov 重做了所有四個類別,增加了功能,修復了錯誤,將它們分成四個單獨的標頭,並添加了 weak_ptr

2003 年 3 月

Peter Dimov、Beman Dawes 和 Greg Colvin 提議shared_ptrweak_ptr 透過第一個函式庫技術報告(稱為 TR1)納入標準函式庫。該提案被接受,並最終成為 2011 年 C++ 標準的一部分。

2007 年 7 月

Peter Dimov 和 Beman Dawes 提議shared_ptr 進行一些增強,因為它正在進入最終成為 C++11 標準的工作草案中。

2012 年 11 月

Glen Fernandes 提供了陣列的 make_sharedallocate_shared 的實作。它們為可以使用建構函式參數或初始值設定項列表初始化的陣列實現了單一分配,以及用於預設初始化和無值初始化的多載。

Peter Dimov 通過語法 shared_ptr<T[]>shared_ptr<T[N]> 擴展了 shared_ptr 來支援陣列,從而幫助了這一開發。

2013 年 4 月

Peter Dimov 提議擴展 shared_ptr 以支援陣列,以納入標準中,並且已被接受。

2014 年 2 月

Glen Fernandes 更新了 make_sharedallocate_shared 以符合 C++ 標準論文 N3870 中的規範,並為陣列和物件實作了 make_unique

Peter Dimov 和 Glen Fernandes 分別更新了純量和陣列實作,以解決 C++ 標準函式庫缺陷 2070。

2017 年 2 月

Glen Fernandes 為陣列重寫了 allocate_sharedmake_shared,以實現更優化和更易於維護的實作。

2017 年 6 月

Peter Dimov 和 Glen Fernandes 以 Asciidoc 格式重寫了文件。

Peter Dimov 添加了 atomic_shared_ptrlocal_shared_ptr

2019 年 8 月

Glen Fernandes 實作了純量和陣列的 allocate_unique

附錄 C:shared_array (已棄用)

注意
此功能已棄用,因為現在可以使用指向 T[]T[N]shared_ptr,並且在各方面都優於此功能。

描述

shared_array 類別模板儲存指向動態分配陣列的指標。(動態分配陣列是使用 C++ new[] 表達式分配的。)保證在指向它的最後一個 shared_array 被銷毀或重置時,將刪除指向的物件。

每個 shared_array 都符合 C++ 標準函式庫的 *CopyConstructible* 和 *Assignable* 要求,因此可以在標準函式庫容器中使用。提供比較運算符,以便 shared_array 可以與標準函式庫的關聯容器一起使用。

通常,shared_array 無法正確地保存指向使用非陣列形式的 new 分配的物件的指標。請參閱 shared_ptr 以了解該用法。

由於實作使用了參考計數,因此不會回收 shared_array 實例的循環。例如,如果 main 持有指向 A 的 shared_array,而 A 直接或間接持有返回 A 的 shared_array,則 A 的使用計數將為 2。銷毀原始 shared_array 將使 A 懸空,使用計數為 1。

指向 std::vectorshared_ptrshared_array 的替代方案,它功能更強大,但更加靈活。

類別模板以 T(指向的物件的型別)作為參數。shared_array 及其大多數成員函式對 T 沒有任何要求;允許它是不完整型別或 void。在下面明確記錄了對成員函式施加額外要求的成員函式(建構函式、重置)。

概要

namespace boost {

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

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

    ~shared_array() noexcept;

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

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

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

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

    explicit operator bool() const noexcept;

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

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

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

成員

element_type

typedef T element_type;
型別

提供已儲存指標的類型。

建構子

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

    建構 shared_array,儲存 p 的副本,p 必須是指向通過 C++ new[] 表達式分配的陣列的指標,或者為 0。之後,使用計數為 1(即使 p == 0;請參閱 ~shared_array)。

    要求

    T 是一個完整的型別。

    擲回

    std::bad_alloc。如果拋出例外,則會呼叫 delete[] p

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

    建構 shared_array,儲存 pd 的副本。之後,使用計數為 1。當需要刪除 p 指向的陣列時,物件 d 用於語句 d(p)

    要求
  • T 是一個完整的型別。

  • D 的複製建構函式和解構函式不得拋出例外。

  • 使用參數 p 呼叫物件 d 不得拋出例外。

    擲回

    std::bad_alloc。如果拋出例外,則會呼叫 d(p)

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

    建構 shared_array,如同儲存 v 中儲存的指標的副本。之後,所有副本的使用計數都比初始使用計數多 1。

    要求

    T 是一個完整的型別。

解構子

~shared_array() noexcept;
  • 效果

    遞減使用計數。然後,如果使用計數為 0,則會刪除儲存的指標指向的陣列。請注意,對值為 0 的指標使用 delete[] 是無害的。

指派

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

    如上所述建構新的 shared_array,然後用新的 shared_array 替換此 shared_array,並銷毀被替換的物件。

    要求

    T 是一個完整的型別。

    回傳

    *this.

reset

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

    如上所述建構新的 shared_array,然後用新的 shared_array 替換此 shared_array,並銷毀被替換的物件。

    要求

    T 是一個完整的型別。

    擲回

    std::bad_alloc。如果拋出例外,則會呼叫 delete[] p

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

    如上所述建構新的 shared_array,然後用新的 shared_array 替換此 shared_array,並銷毀被替換的物件。

    要求
  • T 是一個完整的型別。

  • D 的複製建構函式不得拋出例外。

    擲回

    std::bad_alloc。如果拋出例外,則會呼叫 d(p)

索引

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

儲存的指標所指向的陣列的元素 n 的參考。如果儲存的指標為 0,或者如果 n 小於 0 或大於或等於陣列中的元素數,則行為未定義且幾乎可以肯定是不希望的。

要求

T 是一個完整的型別。

get

T* get() const noexcept;
  • 回傳

    儲存的指標。

unique

bool unique() const noexcept;
  • 回傳

    如果沒有其他 shared_array 共用儲存的指標的所有權,則為 true;否則為 false

use_count

long use_count() const noexcept;
  • 回傳

    共用儲存的指標所有權的 shared_array 物件的數量。

轉換

explicit operator bool() const noexcept;
  • 回傳

    get() != 0.

    要求

    T 是一個完整的型別。

swap

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

    交換兩個智慧型指標的內容。

自由函式

比較

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

    比較兩個智能指標的儲存指標的結果。

注意
提供 operator< 多載以定義順序,以便 shared_array 物件可以在關聯容器(如 std::map)中使用。該實作使用 std::less<T*> 執行比較。這確保了正確處理比較,因為標準規定指標的關係運算未指定(5.9 [expr.rel] 段落 2),但指標的 std::less 已明確定義(20.3.3 [lib.comparisons] 段落 8)。

swap

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

    a.swap(b).

    要求

    T 是一個完整的型別。

此文件是

  • Copyright 1999 Greg Colvin

  • Copyright 1999 Beman Dawes

  • Copyright 2002 Darin Adler

  • Copyright 2003-2020 Peter Dimov

  • Copyright 2005, 2006 Ion Gaztañaga

  • Copyright 2008 Frank Mori Hess

  • Copyright 2012-2017 Glen Fernandes

  • Copyright 2013 Andrey Semashev

並根據 Boost 軟體授權,版本 1.0 發布。