簡介
智慧指標是儲存指向動態配置(堆積)物件之指標的物件。它們的行為很像內建的 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_ptr
和 weak_ptr
自 2011 年的版本以來一直是 C++ 標準的一部分。
此外,該函式庫還包含以下支援公用程式函式和類別
-
make_shared
和allocate_shared
,用於建立物件並傳回shared_ptr
的工廠函式; -
make_unique
,傳回std::unique_ptr
的工廠函式; -
allocate_unique
,一個使用配置器建立物件並傳回std::unique_ptr
的工廠函式; -
enable_shared_from_this
,一個輔助基底類別,可讓您取得指向this
的shared_ptr
; -
enable_shared_from
,一個較新且更好的enable_shared_from_this
替代方案; -
pointer_to_other
,一個用於將一個智慧指標類型轉換為另一個智慧指標類型的輔助特性; -
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_HOOKS
、BOOST_SP_USE_STD_ALLOCATOR
、BOOST_SP_USE_QUICK_ALLOCATOR
、BOOST_AC_USE_SPINLOCK
、BOOST_AC_USE_PTHREADS
、BOOST_SP_USE_SPINLOCK
和BOOST_SP_USE_PTHREADS
啟用的功能已棄用,並且在未來版本中將會移除對其的支援。
1.79.0 版的變更
-
新增
get_allocator_pointer
1.74.0 版的變更
-
為
shared_ptr
、weak_ptr
、local_shared_ptr
新增owner_equals
-
為
shared_ptr
、weak_ptr
新增owner_hash_value
-
新增
owner_equal_to
、owner_hash
-
為
shared_ptr
、local_shared_ptr
新增std::hash
特製化 -
為
weak_ptr
新增boost::hash
支援,以及std::hash
、std::equal_to
特製化
1.72.0 版的變更
-
新增
allocate_unique
1.71.0 版的變更
-
為
weak_ptr
新增別名建構子 -
新增
weak_ptr<T>::empty()
-
新增
enable_shared_from
、shared_from
和weak_from
1.65.0 版的變更
-
新增
atomic_shared_ptr
-
新增
local_shared_ptr
、make_local_shared
scoped_ptr:作用域物件擁有權
描述
scoped_ptr
類別範本儲存指向動態配置物件的指標。(動態配置物件是使用 C++ new
運算式配置的。)保證所指向的物件會被刪除,無論是在 scoped_ptr
解構時,還是透過明確的 reset
。請參閱範例。
scoped_ptr
是簡單需求的簡單解決方案。它提供了一個基本的「資源取得即初始化」設施,沒有共享所有權或轉移所有權語意。它的名稱和語意強制執行(透過不可複製)表示其意圖僅在目前範圍內保留所有權。由於它是不可複製的,因此對於不應複製的指標來說,它比 shared_ptr
更安全。
由於 scoped_ptr
很簡單,因此在通常的實作中,每個操作都與內建指標一樣快,並且空間負擔不超過內建指標。
scoped_ptr
不能在 C++ 標準函式庫容器中使用。如果您需要可以使用的智慧指標,請使用 shared_ptr
或 std::unique_ptr
。
scoped_ptr
無法正確保存指向動態配置陣列的指標。請參閱 scoped_array
以了解該用法。
類別範本以 T
參數化,這是指向的物件類型。摧毀 T
不得擲回例外狀況,且 T
必須在 scoped_ptr<T>::~scoped_ptr
實例化時完成。
概要
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_ptr
或 std::unique_ptr
的主要原因,是要讓程式碼的讀者知道您打算僅將「資源取得即初始化」應用於目前範圍,並且無意轉移所有權。
使用 scoped_ptr
的第二個原因是防止後續維護程式設計師新增一個透過傳回 auto_ptr
來轉移所有權的函式,因為維護程式設計師看到了 auto_ptr
,並假設所有權可以安全地轉移。
想想 bool
與 int
。我們都知道在底層,bool
通常只是一個 int
。事實上,有些人反對在 C++ 標準中包含 bool,因為這樣。但是透過撰寫 bool
而不是 int
,您可以告訴讀者您的意圖。與 scoped_ptr
相同;透過使用它,您正在傳達意圖。
有人建議 scoped_ptr<T>
等同於 std::auto_ptr<T> const
。然而,Ed Brey 指出,reset
無法在 std::auto_ptr<T> const
上運作。
Handle/Body 慣用法
scoped_ptr
的一個常見用法是實作避免在標頭檔中公開主體(實作)的 handle/body(也稱為 pimpl)慣用法。
scoped_ptr_example_test.cpp
範例程式包含一個標頭檔 scoped_ptr_example.hpp
,其中使用 scoped_ptr<>
指向不完整類型以隱藏實作細節。需要完整類型才能實例化的成員函式,會在 scoped_ptr_example.cpp
實作檔案中實例化。
常見問題
-
為什麼
scoped_ptr
沒有release()
成員函式?在閱讀原始碼時,能夠根據使用的類型推斷程式行為是很重要的。如果
scoped_ptr
有release()
成員函式,就會有可能轉移所持有的指標的所有權,削弱它將資源生命週期限制在特定範圍內的角色。如果需要轉移所有權,請使用std::auto_ptr
。(由 Dave Abrahams 提供)
scoped_array:作用域陣列擁有權
描述
scoped_array
類別樣板儲存一個指向動態配置陣列的指標。(動態配置陣列是使用 C++ 的 new[]
表達式配置的。)保證指向的陣列會被刪除,無論是在 scoped_array
解構時,或透過明確的 reset
方法刪除。
scoped_array
樣板是簡單需求的簡單解決方案。它提供基本的「資源取得即初始化」機制,而沒有共享所有權或轉移所有權的語義。其名稱和語義的強制執行(透過不可複製)都表示它打算僅在目前範圍內保留所有權。由於它是不可複製的,因此對於不應被複製的指標而言,它比 shared_ptr<T[]>
更安全。
由於 scoped_array
非常簡單,在其常見實作中,每個操作都與內建陣列指標一樣快,並且空間開銷不比內建陣列指標多。
它不能用於 C++ 標準函式庫容器。如果 scoped_array
不符合您的需求,請參閱 shared_ptr<T[]>
。
它不能正確持有指向單一物件的指標。請參閱 scoped_ptr
以瞭解該用法。
std::vector
是 scoped_array
的替代方案,它功能更強大但更靈活。boost::array
是不使用動態配置的替代方案。
類別樣板以 T
作為參數,T
是指向的物件類型。
概要
scoped_array
定義於 <boost/smart_ptr/scoped_array.hpp>
。
namespace boost {
template<class T> class scoped_array {
private:
scoped_array(scoped_array const &);
scoped_array & operator=(scoped_array const &);
void operator==( scoped_array const& ) const;
void operator!=( scoped_array const& ) const;
public:
typedef T element_type;
explicit scoped_array(T * p = 0) noexcept;
~scoped_array() noexcept;
void reset(T * p = 0) noexcept;
T & operator[](std::ptrdiff_t i) const noexcept;
T * get() const noexcept;
explicit operator bool () const noexcept;
void swap(scoped_array & b) noexcept;
};
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
template<class T>
bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;
template<class T>
bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T>
bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
}
成員
element_type
typedef T element_type;
提供已儲存指標的類型。
建構子
explicit scoped_array(T * p = 0) noexcept;
建構一個 scoped_array
,儲存 p
的副本,p
必須透過 C++ 的 new[]
表達式配置或為 0。T
不必是完整類型。
解構子
~scoped_array() noexcept;
刪除儲存指標所指向的陣列。請注意,對值為 0 的指標執行 delete[]
是無害的。T
必須是完整類型,且對儲存指標執行 delete[]
不得擲回例外。
reset
void reset(T * p = 0) noexcept;
刪除儲存指標所指向的陣列,然後儲存 p
的副本,p
必須透過 C++ 的 new[]
表達式配置或為 0。T
必須是完整類型,且對儲存指標執行 delete[]
不得擲回例外。
下標
T & operator[](std::ptrdiff_t i) const noexcept;
傳回儲存指標所指向陣列中索引 i
元素的參照。如果儲存的指標為 0,或者 i
小於 0 或大於等於陣列中的元素數,則行為未定義且幾乎肯定是不希望發生的。
get
T * get() const noexcept;
傳回儲存的指標。T
不需要是完整的類型。
轉換
explicit operator bool () const noexcept;
傳回 get() != 0
。
注意
|
在 C++03 編譯器上,傳回值是不指定的類型。 |
swap
void swap(scoped_array & b) noexcept;
交換兩個智慧指標的內容。T
不需要是完整的類型。
自由函式
swap
template<class T> void swap(scoped_array<T> & a, scoped_array<T> & b) noexcept;
等同於 a.swap(b)
。
比較
template<class T> bool operator==( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator==( std::nullptr_t, scoped_array<T> const & p ) noexcept;
傳回 p.get() == nullptr
。
template<class T> bool operator!=( scoped_array<T> const & p, std::nullptr_t ) noexcept;
template<class T> bool operator!=( std::nullptr_t, scoped_array<T> const & p ) noexcept;
傳回 p.get() != nullptr
。
shared_ptr:共享擁有權
描述
shared_ptr
類別樣板儲存一個指向動態配置物件的指標,通常使用 C++ 的 new
表達式。當最後一個指向它的 shared_ptr
被銷毀或重設時,保證指向的物件會被刪除。
shared_ptr<X> p1( new X );
shared_ptr<void> p2( new int(5) );
shared_ptr
會刪除在建構時傳遞的確切指標,包括其原始類型,無論樣板參數為何。在上面的第二個範例中,當 p2
被銷毀或重設時,它會對傳遞給建構子的原始 int*
呼叫 delete
,即使 p2
本身是 shared_ptr<void>
類型,並儲存一個 void*
類型的指標。
每個 shared_ptr
都符合 C++ 標準函式庫的 CopyConstructible
、MoveConstructible
、CopyAssignable
和 MoveAssignable
的要求,並且可以用於標準函式庫容器中。提供比較運算子,使 shared_ptr
可以與標準函式庫的關聯容器搭配使用。
由於實作使用參考計數,shared_ptr
實例的循環將不會被回收。例如,如果 main()
持有指向 A
的 shared_ptr
,而 A
直接或間接地持有指向 A
的 shared_ptr
,則 A
的使用計數將為 2。銷毀原始的 shared_ptr
將會使 A
懸空,其使用計數為 1。使用 weak_ptr
來「打破循環」。
類別樣板以 T
作為參數,T
是指向的物件類型。shared_ptr
及其大多數成員函式對 T
沒有任何要求;它可以是不完整類型或 void
。有額外要求的成員函式(建構子、reset
)將在下面明確說明。
只要 T*
可以隱式轉換為 U*
,shared_ptr<T>
就可以隱式轉換為 shared_ptr<U>
。特別是,shared_ptr<T>
可以隱式轉換為 shared_ptr<T const>
,轉換為 U
是 T
可存取基底的 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[]
可以對索引執行範圍檢查。
shared_ptr<double[1024]> p1( new double[1024] );
shared_ptr<double[]> p2( new double[n] );
最佳實踐
一個幾乎消除記憶體洩漏可能性的簡單指南是:始終使用具名的智慧型指標變數來持有 new
的結果。程式碼中每個出現的 new
關鍵字都應具有以下形式
shared_ptr<T> p(new Y);
當然,可以接受使用另一個智慧型指標來取代上面的 shared_ptr
;讓 T
和 Y
為相同類型,或將引數傳遞給 Y
的建構子也是可以的。
如果您遵守此指南,自然而然地,您將不會有明確的 delete
陳述式;try
/catch
結構會很少見。
避免使用未命名的 shared_ptr
暫時物件來節省輸入;若要了解為何這很危險,請考慮此範例
void f(shared_ptr<int>, int);
int g();
void ok()
{
shared_ptr<int> p( new int(2) );
f( p, g() );
}
void bad()
{
f( shared_ptr<int>( new int(2) ), g() );
}
函式 ok
完全遵循指南,而 bad
原地建構暫時的 shared_ptr
,導致可能發生記憶體洩漏。由於函式引數是以未指定的順序評估,因此有可能先評估 new int(2)
,第二個評估 g()
,如果 g
擲回例外,我們可能永遠不會到達 shared_ptr
建構子。有關更多資訊,請參閱 Herb Sutter 的處理方式。
也可以透過使用 <boost/smart_ptr/make_shared.hpp>
中定義的 make_shared
或 allocate_shared
工廠函式來消除上述的異常安全問題。這些工廠函式還可以透過合併配置來提供效率上的好處。
概要
shared_ptr
定義於 <boost/smart_ptr/shared_ptr.hpp>
。
namespace boost {
class bad_weak_ptr: public std::exception;
template<class T> class weak_ptr;
template<class T> class shared_ptr {
public:
typedef /*see below*/ element_type;
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
~shared_ptr() noexcept;
shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r, element_type * p) noexcept;
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
template<class Y> explicit shared_ptr(std::auto_ptr<Y> & r);
template<class Y> shared_ptr(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(shared_ptr const & r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r) noexcept;
shared_ptr & operator=(shared_ptr const && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> const && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> & r);
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
shared_ptr & operator=(std::nullptr_t) noexcept;
void reset() noexcept;
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
T & operator*() const noexcept; // only valid when T is not an array type
T * operator->() const noexcept; // only valid when T is not an array type
// only valid when T is an array type
element_type & operator[](std::ptrdiff_t i) const noexcept;
element_type * get() const noexcept;
bool unique() const noexcept;
long use_count() const noexcept;
explicit operator bool() const noexcept;
void swap(shared_ptr & b) noexcept;
template<class Y> bool owner_before(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_before(weak_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(weak_ptr<Y> const & r) const noexcept;
std::size_t owner_hash_value() const noexcept;
};
template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
template<class T> void swap(shared_ptr<T> & a, shared_ptr<T> & b) noexcept;
template<class T>
typename shared_ptr<T>::element_type *
get_pointer(shared_ptr<T> const & p) noexcept;
template<class T, class U>
shared_ptr<T> static_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> const_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> dynamic_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class T, class U>
shared_ptr<T> reinterpret_pointer_cast(shared_ptr<U> const & r) noexcept;
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, shared_ptr<Y> const & p);
template<class D, class T> D * get_deleter(shared_ptr<T> const & p) noexcept;
template<class T> bool atomic_is_lock_free( shared_ptr<T> const * p ) noexcept;
template<class T> shared_ptr<T> atomic_load( shared_ptr<T> const * p ) noexcept;
template<class T>
shared_ptr<T> atomic_load_explicit( shared_ptr<T> const * p, int ) noexcept;
template<class T>
void atomic_store( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
void atomic_store_explicit( shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange( shared_ptr<T> * p, shared_ptr<T> r ) noexcept;
template<class T>
shared_ptr<T> atomic_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> r, int ) noexcept;
template<class T>
bool atomic_compare_exchange(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w ) noexcept;
template<class T>
bool atomic_compare_exchange_explicit(
shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w, int, int ) noexcept;
}
成員
element_type
typedef ... element_type;
當 T
不是陣列類型時,element_type
為 T
,當 T
為 U[]
或 U[N]
時,則為 U
。
預設建構子
constexpr shared_ptr() noexcept;
constexpr shared_ptr(std::nullptr_t) noexcept;
-
- 效果
-
建構一個空的
shared_ptr
。 - 後置條件
-
use_count() == 0 && get() == 0
.
指標建構子
template<class Y> explicit shared_ptr(Y * p);
-
- 要求
-
Y
必須是完整類型。當T
是陣列類型時,表達式delete[] p
,或當T
不是陣列類型時,表達式delete p
,必須是格式良好、定義明確且不會擲回例外的。當T
是U[N]
時,Y(*)[N]
必須可轉換為T*
;當T
是U[]
時,Y(*)[]
必須可轉換為T*
;否則,Y*
必須可轉換為T*
。 - 效果
-
當
T
不是陣列類型時,建構一個擁有指標p
的shared_ptr
。否則,建構一個擁有p
和一個未指定類型的刪除器的shared_ptr
,該刪除器會呼叫delete[] p
。 - 後置條件
-
use_count() == 1 && get() == p
。如果T
不是陣列類型,且p
可明確轉換為某些V
的enable_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
必須是CopyConstructible
。D
的複製建構子和解構子不得擲回例外。表達式d(p)
必須是格式良好、定義明確且不會擲回例外的。A
必須是 C++ 標準 [allocator.requirements] 章節中所述的Allocator
。當T
是U[N]
時,Y(*)[N]
必須可轉換為T*
;當T
是U[]
時,Y(*)[]
必須可轉換為T*
;否則,Y*
必須可轉換為T*
。 - 效果
-
建構一個擁有指標
p
和刪除器d
的shared_ptr
。接受配置器的建構子會使用a
的副本來配置記憶體。 - 後置條件
-
use_count() == 1 && get() == p
。如果T
不是陣列類型,且p
可明確轉換為某些V
的enable_shared_from_this<V>*
,則p->shared_from_this()
會傳回*this
的副本。 - 擲回
-
std::bad_alloc
,或當無法取得記憶體以外的資源時,實作定義的例外。 - 異常安全
-
如果擲回例外,則會呼叫
d(p)
。
注意
|
當需要刪除 p 所指向的物件時,會使用儲存的 p 副本作為引數來叫用儲存的 d 副本。 |
注意
|
自訂的釋放器允許回傳 shared_ptr 的工廠函式將使用者與其記憶體配置策略隔離。由於釋放器不是型別的一部分,變更配置策略不會破壞原始碼或二進位的相容性,也不需要用戶端重新編譯。例如,當回傳 shared_ptr 到靜態配置的物件時,「無操作」的釋放器很有用,而其他變體則允許將 shared_ptr 用作另一個智慧型指標的包裝器,以簡化互通性。 |
注意
|
D 的複製建構子不得擲出例外,此要求來自於傳值呼叫。如果複製建構子擲出例外,指標將會洩漏。 |
複製和轉換建構子
shared_ptr(shared_ptr const & r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> const & r) noexcept;
-
- 要求
-
Y*
應該可以轉換為T*
。 - 效果
-
如果
r
是空的,則建構一個空的shared_ptr
;否則,建構一個與r
共用所有權的shared_ptr
。 - 後置條件
-
get() == r.get() && use_count() == r.use_count()
.
移動建構子
shared_ptr(shared_ptr && r) noexcept;
template<class Y> shared_ptr(shared_ptr<Y> && r) noexcept;
-
- 要求
-
Y*
應該可以轉換為T*
。 - 效果
-
從
r
移動建構一個shared_ptr
。 - 後置條件
-
*this
包含r
的舊值。r
是空的且r.get() == 0
。
別名建構子
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
從
r
複製建構一個shared_ptr
,同時儲存p
取代之。 - 後置條件
-
get() == p && use_count() == r.use_count()
.
別名移動建構子
template<class Y> shared_ptr(shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
從
r
移動建構一個shared_ptr
,同時儲存p
取代之。 - 後置條件
-
get() == p
且use_count()
等於r
的舊計數。r
是空的且r.get() == 0
。
weak_ptr 建構子
template<class Y> explicit shared_ptr(weak_ptr<Y> const & r);
-
- 要求
-
Y*
應該可以轉換為T*
。 - 效果
-
建構一個與
r
共用所有權的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 & operator=(shared_ptr && r) noexcept;
template<class Y> shared_ptr & operator=(shared_ptr<Y> && r) noexcept;
template<class Y> shared_ptr & operator=(std::auto_ptr<Y> && r);
template<class Y, class D> shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- 效果
-
等同於
shared_ptr(std::move(r)).swap(*this)
。 - 回傳
-
*this
.
shared_ptr & operator=(std::nullptr_t) noexcept;
-
- 效果
-
等同於
shared_ptr().swap(*this)
。 - 回傳
-
*this
.
reset
void reset() noexcept;
-
- 效果
-
等同於
shared_ptr().swap(*this)
。
template<class Y> void reset(Y * p);
-
- 效果
-
等同於
shared_ptr(p).swap(*this)
。
template<class Y, class D> void reset(Y * p, D d);
-
- 效果
-
等同於
shared_ptr(p, d).swap(*this)
。
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- 效果
-
等同於
shared_ptr(p, d, a).swap(*this)
。
template<class Y> void reset(shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
等同於
shared_ptr(r, p).swap(*this)
。
template<class Y> void reset(shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
等同於
shared_ptr(std::move(r), p).swap(*this)
。
間接
T & operator*() const noexcept;
-
- 要求
-
T
不應該是陣列型別。儲存的指標不得為 0。 - 回傳
-
*get()
.
T * operator->() const noexcept;
-
- 要求
-
T
不應該是陣列型別。儲存的指標不得為 0。 - 回傳
-
get()
.
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- 要求
-
T
應該是陣列型別。儲存的指標不得為 0。i >= 0
。如果T
是U[N]
,則i < N
。 - 回傳
-
get()[i]
.
get
element_type * get() const noexcept;
-
- 回傳
-
儲存的指標。
unique
bool unique() const noexcept;
-
- 回傳
-
use_count() == 1
.
use_count
long use_count() const noexcept;
-
- 回傳
-
與
*this
共用所有權的shared_ptr
物件數量,包括*this
,或者當*this
是空的時候為 0。
轉換
explicit operator bool() const noexcept;
-
- 回傳
-
get() != 0
.
注意此轉換運算子允許在布林上下文中使用 shared_ptr
物件,例如if(p && p->valid()) {}
。
注意
|
轉換為 bool 不僅僅是語法糖。它允許在使用 dynamic_pointer_cast 或 weak_ptr::lock 時,在條件中宣告 shared_ptr 變數。 |
注意
|
在 C++03 編譯器上,傳回值是不指定的類型。 |
swap
void swap(shared_ptr & b) noexcept;
-
- 效果
-
交換兩個智慧型指標的內容。
owner_before
template<class Y> bool owner_before(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_before(weak_ptr<Y> const & r) const noexcept;
-
- 回傳
-
請參閱
operator<
的描述。
owner_equals
template<class Y> bool owner_equals(shared_ptr<Y> const & r) const noexcept;
template<class Y> bool owner_equals(weak_ptr<Y> const & r) const noexcept;
-
- 回傳
-
當且僅當
*this
和r
共用所有權或兩者皆為空時為true
。
owner_hash_value
std::size_t owner_hash_value() const noexcept;
-
- 回傳
-
未指定的雜湊值,使得共用所有權的兩個實例具有相同的雜湊值。
自由函式
比較
template<class T, class U>
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 回傳
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 回傳
-
a.get() != b.get()
.
template<class T> bool operator==(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, shared_ptr<T> const & p) noexcept;
-
- 回傳
-
p.get() == 0
.
template<class T> bool operator!=(shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, shared_ptr<T> const & p) noexcept;
-
- 回傳
-
p.get() != 0
.
template<class T, class U>
bool operator<(shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
-
- 回傳
-
未指定的值,使得
-
operator<
是 C++ 標準 [lib.alg.sorting] 節中所述的嚴格弱排序; -
在由
operator<
定義的等價關係下,!(a < b) && !(b < a)
,兩個shared_ptr
實例當且僅當它們共用所有權或兩者皆為空時才等效。
-
注意
|
允許 shared_ptr 物件用作關聯容器中的鍵。 |
注意
|
其餘的比較運算子已省略,因為這是設計。 |
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::vector
和 std::set
。
請注意,在容器填滿後,某些 shared_ptr
物件的使用計數將為 1 而不是 2,因為集合是 std::set
而不是 std::multiset
,因此不包含重複的項目。此外,在執行 push_back
和 insert
容器操作時,使用計數在不同的時間可能會更高。更複雜的是,容器操作可能會在各種情況下擲出例外。如果沒有智慧型指標,在本範例中正確地處理記憶體管理和例外處理將是一場惡夢。
Handle/Body 慣用法
shared_ptr
的一個常見用法是實作控制代碼/主體(也稱為 pimpl)慣用語,以避免在標頭檔中公開主體(實作)。
shared_ptr_example2_test.cpp 範例程式包含一個標頭檔 shared_ptr_example2.hpp,該標頭檔使用指向不完整型別的 shared_ptr
來隱藏實作。需要完整型別的成員函式的實例化發生在 shared_ptr_example2.cpp 實作檔案中。請注意,不需要明確的解構子。與 ~scoped_ptr
不同,~shared_ptr
不要求 T
為完整型別。
執行緒安全
shared_ptr
物件提供與內建型別相同的執行緒安全性層級。多個執行緒可以同時「讀取」(僅使用常數操作存取)shared_ptr
實例。不同的 shared_ptr
實例可以由多個執行緒同時「寫入」(使用可變操作(例如 operator=
或 reset
)存取)(即使這些實例是副本,並且在底層共用相同的參考計數。)
任何其他同時存取都會導致未定義的行為。
範例
shared_ptr<int> p(new int(42));
shared_ptr
// thread A
shared_ptr<int> p2(p); // reads p
// thread B
shared_ptr<int> p3(p); // OK, multiple reads are safe
shared_ptr
實例// thread A
p.reset(new int(1912)); // writes p
// thread B
p2.reset(); // OK, writes p2
shared_ptr
// thread A
p = p3; // reads p3, writes p
// thread B
p3.reset(); // writes p3; undefined, simultaneous read/write
shared_ptr
// thread A
p3 = p2; // reads p2, writes p3
// thread B
// p2 goes out of scope: undefined, the destructor is considered a "write access"
shared_ptr
// thread A
p3.reset(new int(1));
// thread B
p3.reset(new int(2)); // undefined, multiple writes
從 Boost 版本 1.33.0 開始,shared_ptr
在大多數常見平台上使用無鎖定的實作。
如果您的程式是單執行緒的,並且沒有連結到任何可能在其預設配置中使用 shared_ptr
的程式庫,您可以在專案範圍內 #define
巨集 BOOST_SP_DISABLE_THREADS
來切換到普通的非原子參考計數更新。
(在某些但非所有翻譯單元中定義 BOOST_SP_DISABLE_THREADS
在技術上違反了單一定義規則和未定義的行為。儘管如此,實作仍會盡最大努力滿足在這些翻譯單元中使用非原子更新的請求。但無法保證。)
您可以定義巨集 BOOST_SP_USE_PTHREADS
來關閉無鎖定的平台特定實作,並回退到通用的基於 pthread_mutex_t
的程式碼。
常見問題
-
共享指標有幾種不同的變體,具有不同的權衡;為什麼智慧型指標程式庫僅提供單一實作?能夠試驗每種類型以便找到最適合手邊工作的類型會很有用嗎?
shared_ptr
的一個重要目標是提供標準的共享所有權指標。擁有單一指標類型對於穩定的程式庫介面很重要,因為不同的共享指標通常無法互通,即參考計數指標(由程式庫 A 使用)無法與連結指標(由程式庫 B 使用)共用所有權。 -
為什麼 shared_ptr 沒有提供特徵或原則的模板參數來允許廣泛的使用者自訂?
參數化會讓使用者卻步。
shared_ptr
範本經過精心設計,可在不進行廣泛參數化的情況下滿足常見需求。 -
我並不信服。預設參數可以在適當的地方使用來隱藏複雜性。再說一次,為什麼沒有原則?
模板參數會影響型別。請參閱上面第一個問題的答案。
-
為什麼
shared_ptr
不使用連結串列實作?連結串列實作沒有提供足夠的優勢來抵消額外指標的額外成本。此外,要使連結串列實作執行緒安全代價很高。
-
為什麼
shared_ptr
(或任何其他 Boost 智慧型指標)不提供自動轉換為 T*?自動轉換被認為太容易出錯。
-
為什麼
shared_ptr
提供use_count()
?作為撰寫測試案例和除錯顯示的輔助工具。其中一個前身具有
use_count()
,它在追蹤複雜專案中出現的循環相依性錯誤時非常有用。 -
為什麼
shared_ptr
沒有指定複雜度要求?因為複雜度要求會限制實作者,並在沒有明顯益處的情況下使
shared_ptr
使用者的規格變得複雜。例如,如果錯誤檢查的實作必須滿足嚴格的複雜度要求,可能會變得不符合規範。 -
為什麼
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
可能使用自訂的刪除器建立,或者可能指向不同類型的物件。 -
為什麼
operator->()
是 const,但其傳回值是指向元素類型的非 const 指標?淺層複製指標,包括原始指標,通常不會傳播 constness。這樣做沒有什麼意義,因為您總是能從 const 指標取得非 const 指標,然後透過它修改物件。
shared_ptr
盡可能「接近原始指標,但不能更近」。
weak_ptr:非擁有觀察者
描述
weak_ptr
類別樣板儲存對 shared_ptr
已管理的物件的「弱參考」。要存取物件,可以使用接受 weak_ptr
的 shared_ptr
建構函式,或 weak_ptr
成員函式 lock
,將 weak_ptr
轉換為 shared_ptr
。當指向物件的最後一個 shared_ptr
消失且物件被刪除時,嘗試從引用已刪除物件的 weak_ptr
實例取得 shared_ptr
將會失敗:建構函式會擲回 boost::bad_weak_ptr
類型的例外,而 weak_ptr::lock
將會傳回空的 shared_ptr
。
每個 weak_ptr
都符合 C++ 標準程式庫的 CopyConstructible
和 Assignable
要求,因此可以在標準程式庫容器中使用。提供了比較運算子,以便 weak_ptr
可以與標準程式庫的關聯容器一起使用。
weak_ptr
操作永遠不會擲回例外。
類別樣板以 T
作為參數,T
是指向的物件類型。
與 shared_ptr
相比,weak_ptr
提供的操作子集非常有限,因為在多執行緒程式中存取其儲存的指標通常很危險,有時即使在單一執行緒中也不安全(也就是說,它可能會調用未定義的行為)。假設 weak_ptr
有一個傳回原始指標的 get 成員函式,並考慮這段無害的程式碼
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(int * r = q.get())
{
// use *r
}
想像一下,在 if
之後,但在使用 r
之前,另一個執行緒執行了語句 p.reset()
。現在 r
是一個懸空指標。
解決這個問題的方法是從 q
建立一個臨時的 shared_ptr
shared_ptr<int> p(new int(5));
weak_ptr<int> q(p);
// some time later
if(shared_ptr<int> r = q.lock())
{
// use *r
}
現在 r
持有對 q
所指向物件的參考。即使 p.reset()
在另一個執行緒中執行,物件也會保持存活,直到 r
超出範圍或被重設。透過取得指向物件的 shared_ptr
,我們有效地鎖定了它,防止被銷毀。
概要
weak_ptr
定義在 <boost/smart_ptr/weak_ptr.hpp>
中。
namespace boost {
template<class T> class weak_ptr {
public:
typedef /*see below*/ element_type;
weak_ptr() noexcept;
template<class Y> weak_ptr(shared_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr const & r) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r) noexcept;
weak_ptr(weak_ptr && r) noexcept;
template<class Y> weak_ptr(shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> weak_ptr(weak_ptr<Y> && r, element_type * p) noexcept;
~weak_ptr() noexcept;
weak_ptr & operator=(weak_ptr const & r) noexcept;
weak_ptr & operator=(weak_ptr && r) noexcept;
template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r) noexcept;
template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r) noexcept;
long use_count() const noexcept;
bool expired() const noexcept;
bool empty() const noexcept;
shared_ptr<T> lock() const noexcept;
void reset() noexcept;
void swap(weak_ptr<T> & b) noexcept;
template<class Y> bool owner_before( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_before( shared_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( weak_ptr<Y> const & r ) const noexcept;
template<class Y> bool owner_equals( shared_ptr<Y> const & r ) const noexcept;
std::size_t owner_hash_value() const noexcept;
};
template<class T, class U>
bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
}
成員
element_type
typedef ... element_type;
當 T
不是陣列類型時,element_type
為 T
,當 T
為 U[]
或 U[N]
時,則為 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;
-
- 回傳
-
當且僅當
*this
和r
共用所有權或兩者皆為空時為true
。
owner_hash_value
std::size_t owner_hash_value() const noexcept;
-
- 回傳
-
未指定的雜湊值,使得共用所有權的兩個實例具有相同的雜湊值。
自由函式
比較
template<class T, class U>
bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b) noexcept;
-
- 回傳
-
未指定的值,使得
-
operator<
是 C++ 標準 [lib.alg.sorting] 節中所述的嚴格弱排序; -
在
operator<
定義的等價關係下,!(a < b) && !(b < a)
,兩個weak_ptr
實例相等,當且僅當它們共用所有權或兩者都為空時。
-
注意
|
允許 weak_ptr 物件用作關聯容器中的鍵。 |
swap
template<class T> void swap(weak_ptr<T> & a, weak_ptr<T> & b) noexcept;
-
- 效果
-
等同於
a.swap(b)
。
常見問題
-
物件可以在其建構函式中建立指向自身的 weak_ptr 嗎?
不行。
weak_ptr
只能從shared_ptr
建立,而且在物件建構時,還不存在指向該物件的shared_ptr
。即使您可以建立一個指向this
的臨時shared_ptr
,它也會在建構函式結束時超出範圍,而所有weak_ptr
實例都會立即過期。解決方案是將建構函式設為私有,並提供一個傳回
shared_ptr
的工廠函式class X { private: X(); public: static shared_ptr<X> create() { shared_ptr<X> px(new X); // create weak pointers from px here return px; } };
make_shared:建立 shared_ptr
描述
函式樣板 make_shared
和 allocate_shared
提供了方便、安全且有效率的方式來建立 shared_ptr
物件。
原理
一致地使用 shared_ptr
可以消除使用顯式 delete
的需要,但它本身並不能支援避免顯式 new
。使用者不斷要求提供一個工廠函式,該函式會建立給定類型的物件並傳回指向該物件的 shared_ptr
。除了方便和風格之外,這樣的功能也具有例外安全性和顯著的速度優勢,因為它可以針對物件及其對應的控制區塊使用單一配置,從而消除 shared_ptr
建構開銷的重要部分。這消除了關於 shared_ptr
的主要效率抱怨之一。
提供了一系列的超載函式樣板 make_shared
和 allocate_shared
來解決此需求。make_shared
使用全域 operator new
來配置記憶體,而 allocate_shared
使用使用者提供的配置器,從而可以進行更精細的控制。
選擇名稱 make_shared
的理由是,表達式 make_shared<Widget>()
可以大聲朗讀,並傳達預期的含義。
最初,Boost 函式樣板 allocate_shared
和 make_shared
僅適用於純量物件。需要有效率地配置陣列物件。對類別樣板 shared_array
的一個批評一直是缺乏像 make_shared
這樣僅使用單一配置的公用程式。當 shared_ptr
增強為支援陣列類型時,也為陣列類型提供了 allocate_shared
和 make_shared
的其他超載。
概要
make_shared
和 allocate_shared
定義在 <boost/smart_ptr/make_shared.hpp>
中。
namespace boost {
// T is not an array
template<class T, class... Args>
shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
shared_ptr<T> allocate_shared(const A& a, Args&&... args);
// T is an array of unknown bounds
template<class T>
shared_ptr<T> make_shared(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, std::size_t n);
// T is an array of known bounds
template<class T>
shared_ptr<T> make_shared();
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a);
// T is an array of unknown bounds
template<class T> shared_ptr<T>
make_shared(std::size_t n, const remove_extent_t<T>& v);
template<class T, class A> shared_ptr<T>
allocate_shared(const A& a, std::size_t n, const remove_extent_t<T>& v);
// T is an array of known bounds
template<class T>
shared_ptr<T> make_shared(const remove_extent_t<T>& v);
template<class T, class A>
shared_ptr<T> allocate_shared(const A& a, const remove_extent_t<T>& v);
// T is not an array of unknown bounds
template<class T>
shared_ptr<T> make_shared_noinit();
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a);
// T is an array of unknown bounds
template<class T>
shared_ptr<T> make_shared_noinit(std::size_t n);
template<class T, class A>
shared_ptr<T> allocate_shared_noinit(const A& a, std::size_t n);
}
通用要求
除非另有說明,否則適用於所有 make_shared
和 allocate_shared
超載的通用要求如下所述。
- 要求
-
A
應為配置器。A
的複製建構函式和解構函式不得擲回例外。 - 效果
-
配置類型為
T
的物件或n
個類型為U
的物件的記憶體(如果T
是形式為U[]
的陣列類型,並且n
由引數決定,如具體超載所指定)。物件是從引數初始化的,如具體超載所指定。使用a
的重新繫結副本(對於未指定的value_type
)來配置記憶體。如果擲回例外,則函式無效。 - 回傳
-
儲存並擁有新建立物件位址的
shared_ptr
實例。 - 後置條件
-
r.get() != 0
且r.use_count() == 1
,其中r
為傳回值。 - 擲回
-
std::bad_alloc
,從A::allocate
擲回的例外,或從物件的初始化擲回的例外。 - 備註
-
-
執行的記憶體配置不超過一次。這提供的效率等同於侵入式智慧指標。
-
當指定將陣列類型的物件初始化為相同類型的值
v
時,這應解釋為表示該物件的每個陣列元素都會初始化為v
中的對應元素。 -
當指定陣列類型的物件要進行值初始化時,這應解釋為表示該物件的每個陣列元素都會進行值初始化。
-
當指定將非陣列類型
U
的(子)物件初始化為值v
,或從args...
建構時,make_shared
應透過表達式::new(p) U(expr)
執行此初始化(其中expr
是v
或std::forward<Args>(args)...)
,而p
的類型為void*
,並指向適合儲存類型為U
的物件的儲存體。 -
當指定將非陣列類型
U
的(子)物件初始化為值v
,或從args...
建構時,allocate_shared
應透過表達式std::allocator_traits<A2>::construct(a2, p, expr)
執行此初始化(其中expr
是v
或std::forward<Args>(args)...)
,p
指向適合儲存類型為U
的物件的儲存體,而類型為A2
的a2
是a
的潛在重新繫結副本。 -
當指定將非陣列類型
U
的(子)物件進行預設初始化時,make_shared_noinit
和allocate_shared_noinit
應透過表達式::new(p) U
執行此初始化,其中p
的類型為void*
,並指向適合儲存類型為U
的物件的儲存體。 -
當指定將非陣列類型
U
的(子)物件進行值初始化時,make_shared
應透過表達式::new(p) U()
執行此初始化,其中p
的類型為void*
,並指向適合儲存類型為U
的物件的儲存體。 -
當指定將非陣列類型
U
的(子)物件進行值初始化時,allocate_shared
應透過表達式std::allocator_traits<A2>::construct(a2, p)
執行此初始化,其中p
指向適合儲存類型為U
的物件的儲存體,而類型為A2
的a2
是a
的潛在重新繫結副本。 -
陣列元素會依照其位址的升序進行初始化。
-
當傳回值所管理的物件的生命週期結束時,或當陣列元素的初始化擲回例外時,已初始化的元素應依其建構的相反順序銷毀。
-
注意
|
這些函式通常會配置比元素物件總大小更多的記憶體,以容納內部簿記結構,例如參考計數。 |
自由函式
template<class T, class... Args>
shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
shared_ptr<T> allocate_shared(const A& a, Args&&... args);
-
- 約束
-
T
不是陣列。 - 回傳
-
指向類型為
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_ptr
或 weak_ptr
。
enable_shared_from_this<T>
定義了兩個名為 shared_from_this
的成員函式,它們會根據常數性,回傳指向 this
的 shared_ptr<T>
和 shared_ptr<T const>
。它也定義了兩個名為 weak_from_this
的成員函式,它們會回傳對應的 weak_ptr
。
範例
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>
class Y: public boost::enable_shared_from_this<Y>
{
public:
boost::shared_ptr<Y> f()
{
return shared_from_this();
}
};
int main()
{
boost::shared_ptr<Y> p(new Y);
boost::shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
概要
enable_shared_from_this
定義在 <boost/smart_ptr/enable_shared_from_this.hpp>
中。
namespace boost {
template<class T> class enable_shared_from_this {
private:
// exposition only
weak_ptr<T> weak_this_;
protected:
enable_shared_from_this() = default;
~enable_shared_from_this() = default;
enable_shared_from_this(const enable_shared_from_this&) noexcept;
enable_shared_from_this& operator=(const enable_shared_from_this&) noexcept;
public:
shared_ptr<T> shared_from_this();
shared_ptr<T const> shared_from_this() const;
weak_ptr<T> weak_from_this() noexcept;
weak_ptr<T const> weak_from_this() const noexcept;
}
}
成員
enable_shared_from_this(enable_shared_from_this const &) noexcept;
-
- 效果
-
預設建構
weak_this_
。
注意weak_this_
*不會* 從引數複製。
enable_shared_from_this& operator=(enable_shared_from_this const &) noexcept;
-
- 回傳
-
*this
.
注意weak_this_
不會變更。
template<class T> shared_ptr<T> shared_from_this();
template<class T> shared_ptr<T const> shared_from_this() const;
-
- 回傳
-
shared_ptr<T>(weak_this_)
.
注意當 *this
不被shared_ptr
擁有時,這些成員會拋出bad_weak_ptr
。
注意
|
當
|
template<class T> weak_ptr<T> weak_from_this() noexcept;
template<class T> weak_ptr<T const> weak_from_this() const noexcept;
-
- 回傳
-
weak_this_
.
注意與 shared_from_this()
不同,weak_from_this()
在解構子中是有效的,並且會回傳一個expired()
的weak_ptr
,但它仍然與其他參考該物件的weak_ptr
實例(如果有的話)共享所有權。
enable_shared_from
描述
enable_shared_from
作為基底類別使用,允許藉由使用 shared_from
和 weak_from
函式,取得指向物件的原始指標的 shared_ptr
或 weak_ptr
。
enable_shared_from
與 enable_shared_from_this<T>
的不同之處在於它不是樣板,並且是建議用於新程式碼的替代方案。
範例
#include <boost/smart_ptr/enable_shared_from.hpp>
#include <boost/shared_ptr.hpp>
#include <cassert>
class Y: public boost::enable_shared_from
{
public:
boost::shared_ptr<Y> f()
{
return boost::shared_from( this );
}
};
int main()
{
boost::shared_ptr<Y> p(new Y);
boost::shared_ptr<Y> q = p->f();
assert(p == q);
assert(!(p < q || q < p)); // p and q must share ownership
}
概要
enable_shared_from
定義在 <boost/smart_ptr/enable_shared_from.hpp>
中。
namespace boost {
class enable_shared_from: public enable_shared_from_this<enable_shared_from>
{
};
template<class T> shared_ptr<T> shared_from( T * p );
template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
}
函式
template<class T> shared_ptr<T> shared_from( T * p );
-
- 回傳
-
shared_ptr<T>( p->enable_shared_from::shared_from_this(), p )
.
注意當 p
不被shared_ptr
擁有時,會拋出bad_weak_ptr
。
template<class T> weak_ptr<T> weak_from( T * p ) noexcept;
-
- 回傳
-
weak_ptr<T>( p->enable_shared_from::weak_from_this(), p )
.
注意與 shared_from(this)
不同,weak_from(this)
在解構子中是有效的,並且會回傳一個expired()
的weak_ptr
,但它仍然與其他參考該物件的weak_ptr
實例(如果有的話)共享所有權。
make_unique:建立 unique_ptr
描述
make_unique
函式樣板提供方便且安全的方式來建立 std::unique_ptr
物件。
原理
C++11 標準引入了 std::unique_ptr
,但沒有提供任何像 std::make_shared
一樣提供相同異常安全性與避免撰寫 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_unique
和 allocate_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
分別為v
或std::forward<Args>(args)...)
),p
指向適合容納型別U
物件的儲存空間,且型別為A2
的a2
是a
的可能重新繫結的複本。 -
當指定將非陣列型別
U
的(子)物件預設初始化時,allocate_unique_noinit
應透過運算式::new(p) U
執行此初始化,其中p
的型別為void*
且指向適合容納型別U
物件的儲存空間。 -
當指定將非陣列型別
U
的(子)物件數值初始化時,allocate_unique
應透過運算式std::allocator_traits<A2>::construct(a2, p)
執行此初始化,其中p
指向適合容納型別U
物件的儲存空間,且型別為A2
的a2
是a
的可能重新繫結的複本。 -
陣列元素會依照其位址的升序進行初始化。
-
當傳回值所管理的物件的生命週期結束時,或當陣列元素的初始化擲回例外時,已初始化的元素應依其建構的相反順序銷毀。
-
自由函式
template<class T, class A, class... Args>
std::unique_ptr<T, alloc_deleter<T, A>>
allocate_unique(const A& a, Args&&... args);
-
- 約束
-
T
不是陣列。 - 回傳
-
指向型別為
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_deleter
是 allocate_unique
函式使用的刪除器。
概要
template<class T, class A>
class alloc_deleter {
public:
using pointer =
unspecified
;
explicit alloc_deleter(const A& a) noexcept;
void operator()(pointer p);
};
成員
using pointer =
unspecified
;
-
滿足 *NullablePointer* 的型別。
explicit alloc_deleter(const A& a) noexcept;
-
- 效果
-
從
a
初始化儲存的分配器。
void operator()(pointer p);
-
- 效果
-
使用儲存的分配器,銷毀物件並解除配置
p
所參考的儲存空間。
intrusive_ptr:管理具有內嵌計數的物件
描述
intrusive_ptr
類別樣板儲存一個指向帶有嵌入式參考計數的物件的指標。每個新的 intrusive_ptr
實例都會透過對函式 intrusive_ptr_add_ref
的非限定呼叫來遞增參考計數,並將指標作為引數傳遞給它。同樣地,當 intrusive_ptr
被銷毀時,它會呼叫 intrusive_ptr_release
;當其參考計數降為零時,此函式負責銷毀物件。使用者應提供這兩個函式的適當定義。在支援引數相依查找的編譯器上,intrusive_ptr_add_ref
和 intrusive_ptr_release
應在對應於其參數的命名空間中定義;否則,這些定義需要放入命名空間 boost
中。此程式庫提供了一個輔助基底類別樣板 intrusive_ref_counter
,它可能協助為使用者型別新增 intrusive_ptr
的支援。
類別樣板以 T
作為參數,T
是所指向物件的型別。只要 T*
可以隱含轉換為 U*
,intrusive_ptr<T>
就可以隱含轉換為 intrusive_ptr<U>
。
使用 intrusive_ptr
的主要原因有:
-
某些現有的框架或作業系統提供帶有嵌入式參考計數的物件;
-
intrusive_ptr
的記憶體佔用量與對應的原始指標相同; -
intrusive_ptr<T>
可以從型別為T*
的任意原始指標建構。
作為一般規則,如果不太明顯 intrusive_ptr
比 shared_ptr
更適合您的需求,請先嘗試使用基於 shared_ptr
的設計。
概要
intrusive_ptr
定義在 <boost/smart_ptr/intrusive_ptr.hpp>
中。
namespace boost {
template<class T> class intrusive_ptr {
public:
typedef T element_type;
intrusive_ptr() noexcept;
intrusive_ptr(T * p, bool add_ref = true);
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
~intrusive_ptr();
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
void reset();
void reset(T * r);
void reset(T * r, bool add_ref);
T & operator*() const noexcept;
T * operator->() const noexcept;
T * get() const noexcept;
T * detach() noexcept;
explicit operator bool () const noexcept;
void swap(intrusive_ptr & b) noexcept;
};
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(intrusive_ptr<T> const & a, U * b) noexcept;
template<class T, class U>
bool operator!=(intrusive_ptr<T> const & a, U * b) noexcept;
template<class T, class U>
bool operator==(T * a, intrusive_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(T * a, intrusive_ptr<U> const & b) noexcept;
template<class T>
bool operator<(intrusive_ptr<T> const & a, intrusive_ptr<T> const & b) noexcept;
template<class T> void swap(intrusive_ptr<T> & a, intrusive_ptr<T> & b) noexcept;
template<class T> T * get_pointer(intrusive_ptr<T> const & p) noexcept;
template<class T, class U>
intrusive_ptr<T> static_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class T, class U>
intrusive_ptr<T> const_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class T, class U>
intrusive_ptr<T> dynamic_pointer_cast(intrusive_ptr<U> const & r) noexcept;
template<class E, class T, class Y>
std::basic_ostream<E, T> & operator<< (std::basic_ostream<E, T> & os,
intrusive_ptr<Y> const & p);
}
成員
element_type
typedef T element_type;
提供樣板參數 T 的型別。
建構子
intrusive_ptr() noexcept;
-
- 後置條件
-
get() == 0
.
intrusive_ptr(T * p, bool add_ref = true);
-
- 效果
-
if(p != 0 && add_ref) intrusive_ptr_add_ref(p);
. - 後置條件
-
get() == p
.
intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
-
- 效果
-
T * p = r.get(); if(p != 0) intrusive_ptr_add_ref(p);
. - 後置條件
-
get() == r.get()
.
intrusive_ptr(intrusive_ptr && r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> && r);
-
- 後置條件
-
get()
等於r.get()
的舊值。r.get() == 0
。
解構子
~intrusive_ptr();
-
- 效果
-
if(get() != 0) intrusive_ptr_release(get());
.
賦值
intrusive_ptr & operator=(intrusive_ptr const & r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
intrusive_ptr & operator=(T * r);
-
- 效果
-
等同於
intrusive_ptr(r).swap(*this)
。 - 回傳
-
*this
.
intrusive_ptr & operator=(intrusive_ptr && r);
template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> && r);
-
- 效果
-
等同於
intrusive_ptr(std::move(r)).swap(*this)
。 - 回傳
-
*this
.
reset
void reset();
-
- 效果
-
等同於
intrusive_ptr().swap(*this)
。
void reset(T * r);
-
- 效果
-
等同於
intrusive_ptr(r).swap(*this)
。
void reset(T * r, bool add_ref);
-
- 效果
-
等同於
intrusive_ptr(r, add_ref).swap(*this)
。
間接
T & operator*() const noexcept;
-
- 需求
-
get() != 0
. - 回傳
-
*get()
.
T * operator->() const noexcept;
-
- 需求
-
get() != 0
. - 回傳
-
get()
.
get
T * get() const noexcept;
-
- 回傳
-
儲存的指標。
detach
T * detach() noexcept;
-
- 回傳
-
儲存的指標。
- 後置條件
-
get() == 0
.
注意
|
傳回的指標具有增加的參考計數。這允許將 intrusive_ptr 轉換回原始指標,而無需獲取和釋放額外參考的效能開銷。它可以被視為非參考遞增建構子的補充。 |
注意
|
使用 detach 會逃脫 intrusive_ptr 提供的自動參考計數的安全性。它應該僅在絕對必要時使用(例如,當與現有 API 介面時),並且在徹底理解其含義時使用。 |
轉換
explicit operator bool () const noexcept;
-
- 回傳
-
get() != 0
.
注意此轉換運算子允許在布林上下文中使用 intrusive_ptr
物件,例如if (p && p->valid()) {}
。
注意
|
在 C++03 編譯器上,傳回值是不指定的類型。 |
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_ref
和 intrusive_ptr_release
函式,它們會根據需要修改參考計數器,並在計數器降至零時銷毀使用者的物件。
類別樣板以 Derived
和 CounterPolicy
參數為參數。第一個參數是從 intrusive_ref_counter
派生的使用者類別。當沒有對它的參考時,需要此類型才能正確銷毀物件。
第二個參數是定義參考計數器性質的策略。程式庫提供兩個此類策略:thread_unsafe_counter
和 thread_safe_counter
。前者指示 intrusive_ref_counter
基底類別使用僅適用於單執行緒使用的計數器。指向使用此類參考計數器的單個物件的指標不得在不同的執行緒中使用。後者策略使參考計數器成為執行緒安全的,除非目標平台不支援執行緒。由於在現代系統中對執行緒的支援很常見,因此預設的計數器策略是 thread_safe_counter
。
概要
intrusive_ref_counter
定義於 <boost/smart_ptr/intrusive_ref_counter.hpp>
。
namespace boost {
struct thread_unsafe_counter;
struct thread_safe_counter;
template<class Derived, class CounterPolicy = thread_safe_counter>
class intrusive_ref_counter {
public:
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter& v) noexcept;
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
unsigned int use_count() const noexcept;
protected:
~intrusive_ref_counter() = default;
};
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
}
成員
建構子
intrusive_ref_counter() noexcept;
intrusive_ref_counter(const intrusive_ref_counter&) noexcept;
-
- 後置條件
-
use_count() == 0
.
注意建構物件的指標預期會傳遞給 intrusive_ptr
建構子、指派運算子或reset
方法,這將遞增參考計數器。
解構子
~intrusive_ref_counter();
-
- 效果
-
銷毀計數器物件。
注意解構子是受保護的,因此物件只能透過 Derived
類別銷毀。
指派
intrusive_ref_counter& operator=(const intrusive_ref_counter& v) noexcept;
-
- 效果
-
不執行任何操作,參考計數器不會被修改。
use_count
unsigned int use_count() const noexcept;
-
- 回傳
-
參考計數器的目前值。
注意傳回的值在多執行緒應用程式中可能不是實際值。
自由函式
intrusive_ptr_add_ref
template<class Derived, class CounterPolicy>
void intrusive_ptr_add_ref(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- 效果
-
遞增參考計數器。
intrusive_ptr_release
template<class Derived, class CounterPolicy>
void intrusive_ptr_release(
const intrusive_ref_counter<Derived, CounterPolicy>* p) noexcept;
-
- 效果
-
遞減參考計數器。如果參考計數器達到 0,則呼叫
delete static_cast<const Derived*>(p)
。
local_shared_ptr:單一執行緒內的共享擁有權
描述
local_shared_ptr
幾乎與 shared_ptr
相同,唯一值得注意的區別是它的參考計數使用非原子操作更新。因此,local_shared_ptr
及其所有副本必須駐留在(本地於)單個執行緒中(因此得名)。
local_shared_ptr
可以轉換為 shared_ptr
,反之亦然。從 shared_ptr
建立 local_shared_ptr
會建立新的本地參考計數;這表示從同一個 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
會導致兩者共用相同的計數
shared_ptr<X> p1( new X );
local_shared_ptr<X> p2( p1 ); // p2.local_use_count() == 1
local_shared_ptr<X> p3( p2 ); // p3.local_use_count() == 2
從同一個 local_shared_ptr
建立的兩個 shared_ptr
實例確實共用所有權
local_shared_ptr<X> p1( new X );
shared_ptr<X> p2( p1 ); // p2.use_count() == 2
shared_ptr<X> p3( p1 ); // p3.use_count() == 3
這裡 p2.use_count()
為 2,因為 p1
也持有參考。
可以將 local_shared_ptr<T>
視為 shared_ptr<shared_ptr<T>>
,其中外部的 shared_ptr
對其計數使用非原子操作。從 local_shared_ptr
轉換為 shared_ptr
會產生內部 shared_ptr
的副本;從 shared_ptr
轉換會將其包裝到具有非原子使用計數的外部 shared_ptr
中(概念上而言),並傳回結果。
概要
local_shared_ptr
定義於 <boost/smart_ptr/local_shared_ptr.hpp>
。
namespace boost {
template<class T> class local_shared_ptr {
public:
typedef /*see below*/ element_type;
// constructors
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
template<class Y> explicit local_shared_ptr(Y * p);
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
// destructor
~local_shared_ptr() noexcept;
// assignment
local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
local_shared_ptr & operator=(local_shared_ptr const && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
local_shared_ptr & operator=(std::nullptr_t) noexcept;
// reset
void reset() noexcept;
template<class Y> void reset(Y * p);
template<class Y, class D> void reset(Y * p, D d);
template<class Y, class D, class A> void reset(Y * p, D d, A a);
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
// accessors
T & operator*() const noexcept; // only valid when T is not an array type
T * operator->() const noexcept; // only valid when T is not an array type
// only valid when T is an array type
element_type & operator[](std::ptrdiff_t i) const noexcept;
element_type * get() const noexcept;
long local_use_count() const noexcept;
// conversions
explicit operator bool() const noexcept;
template<class Y> operator shared_ptr<Y>() const noexcept;
template<class Y> operator weak_ptr<Y>() const noexcept;
// swap
void swap(local_shared_ptr & b) noexcept;
// owner_before
template<class Y> bool owner_before(local_shared_ptr<Y> const & r) const noexcept;
// owner_equals
template<class Y> bool owner_equals(local_shared_ptr<Y> const & r) const noexcept;
};
// comparisons
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
// swap
template<class T> void swap(local_shared_ptr<T> & a, local_shared_ptr<T> & b) noexcept;
// get_pointer
template<class T>
typename local_shared_ptr<T>::element_type *
get_pointer(local_shared_ptr<T> const & p) noexcept;
// casts
template<class T, class U>
local_shared_ptr<T> static_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> const_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> dynamic_pointer_cast(local_shared_ptr<U> const & r) noexcept;
template<class T, class U>
local_shared_ptr<T> reinterpret_pointer_cast(local_shared_ptr<U> const & r) noexcept;
// stream I/O
template<class E, class T, class Y>
std::basic_ostream<E, T> &
operator<< (std::basic_ostream<E, T> & os, local_shared_ptr<Y> const & p);
// get_deleter
template<class D, class T> D * get_deleter(local_shared_ptr<T> const & p) noexcept;
}
成員
element_type
typedef ... element_type;
當 T
不是陣列類型時,element_type
為 T
,當 T
為 U[]
或 U[N]
時,則為 U
。
預設建構子
constexpr local_shared_ptr() noexcept;
constexpr local_shared_ptr(std::nullptr_t) noexcept;
-
- 效果
-
建構空的
local_shared_ptr
。 - 後置條件
-
local_use_count() == 0 && get() == 0
.
指標建構子
template<class Y> explicit local_shared_ptr(Y * p);
-
- 效果
-
建構擁有
shared_ptr<T>( p )
的local_shared_ptr
。 - 後置條件
-
local_use_count() == 1 && get() == p
. - 擲回
-
std::bad_alloc
,或當無法取得記憶體以外的資源時,實作定義的例外。
接受刪除器的建構子
template<class Y, class D> local_shared_ptr(Y * p, D d);
template<class D> local_shared_ptr(std::nullptr_t p, D d);
-
- 效果
-
建構擁有
shared_ptr<T>( p, d )
的local_shared_ptr
。 - 後置條件
-
local_use_count() == 1 && get() == p
. - 擲回
-
std::bad_alloc
,或當無法取得記憶體以外的資源時,實作定義的例外。
template<class Y, class D, class A> local_shared_ptr(Y * p, D d, A a);
template<class D, class A> local_shared_ptr(std::nullptr_t p, D d, A a);
-
- 效果
-
建構擁有
shared_ptr<T>( p, d, a )
的local_shared_ptr
。 - 後置條件
-
local_use_count() == 1 && get() == p
. - 擲回
-
std::bad_alloc
,或當無法取得記憶體以外的資源時,實作定義的例外。
複製和轉換建構子
local_shared_ptr(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r) noexcept;
-
- 要求
-
Y*
應該可以轉換為T*
。 - 效果
-
如果
r
為空,則建構空的local_shared_ptr
;否則,建構與r
共用所有權的local_shared_ptr
。 - 後置條件
-
get() == r.get() && local_use_count() == r.local_use_count()
.
移動建構子
local_shared_ptr(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r) noexcept;
-
- 要求
-
Y*
應該可以轉換為T*
。 - 效果
-
從
r
移動建構local_shared_ptr
。 - 後置條件
-
*this
包含r
的舊值。r
是空的且r.get() == 0
。
shared_ptr 建構子
template<class Y> local_shared_ptr( shared_ptr<Y> const & r );
template<class Y> local_shared_ptr( shared_ptr<Y> && r );
-
- 效果
-
建構擁有
r
的local_shared_ptr
。 - 後置條件
-
local_use_count() == 1
。get()
傳回r.get()
的舊值。 - 擲回
-
std::bad_alloc
,或當無法取得記憶體以外的資源時,實作定義的例外。
別名建構子
template<class Y> local_shared_ptr(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
建構與
r
共用所有權並儲存p
的local_shared_ptr
。 - 後置條件
-
get() == p && local_use_count() == r.local_use_count()
.
別名移動建構子
template<class Y> local_shared_ptr(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
從
r
移動建構local_shared_ptr
,同時改為儲存p
。 - 後置條件
-
get() == p
且local_use_count()
等於r
的舊計數。r
為空且r.get() == 0
。
unique_ptr 建構子
template<class Y, class D> local_shared_ptr(std::unique_ptr<Y, D> && r);
-
- 要求
-
Y*
應該可以轉換為T*
。 - 效果
-
-
當
r.get() == 0
時,等同於local_shared_ptr()
; -
否則,建構擁有
shared_ptr<T>( std::move(r) )
的local_shared_ptr
。
-
- 擲回
-
std::bad_alloc
,或當無法取得記憶體以外的資源時,實作定義的例外。 - 異常安全
-
如果擲出例外,則建構子不會有任何作用。
解構子
~local_shared_ptr() noexcept;
-
- 效果
-
-
如果
*this
為空,或與另一個local_shared_ptr
實例共用所有權(local_use_count() > 1
),則不會有任何副作用。 -
否則,銷毀擁有的
shared_ptr
。
-
賦值
local_shared_ptr & operator=(local_shared_ptr const & r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> const & r) noexcept;
-
- 效果
-
等同於
local_shared_ptr(r).swap(*this)
。 - 回傳
-
*this
.
local_shared_ptr & operator=(local_shared_ptr && r) noexcept;
template<class Y> local_shared_ptr & operator=(local_shared_ptr<Y> && r) noexcept;
template<class Y, class D> local_shared_ptr & operator=(std::unique_ptr<Y, D> && r);
-
- 效果
-
等同於
local_shared_ptr(std::move(r)).swap(*this)
。 - 回傳
-
*this
.
local_shared_ptr & operator=(std::nullptr_t) noexcept;
-
- 效果
-
等同於
local_shared_ptr().swap(*this)
。 - 回傳
-
*this
.
reset
void reset() noexcept;
-
- 效果
-
等同於
local_shared_ptr().swap(*this)
。
template<class Y> void reset(Y * p);
-
- 效果
-
等同於
local_shared_ptr(p).swap(*this)
。
template<class Y, class D> void reset(Y * p, D d);
-
- 效果
-
等同於
local_shared_ptr(p, d).swap(*this)
。
template<class Y, class D, class A> void reset(Y * p, D d, A a);
-
- 效果
-
等同於
local_shared_ptr(p, d, a).swap(*this)
。
template<class Y> void reset(local_shared_ptr<Y> const & r, element_type * p) noexcept;
-
- 效果
-
等同於
local_shared_ptr(r, p).swap(*this)
。
template<class Y> void reset(local_shared_ptr<Y> && r, element_type * p) noexcept;
-
- 效果
-
等同於
local_shared_ptr(std::move(r), p).swap(*this)
。
間接
T & operator*() const noexcept;
-
- 要求
-
T
不應為陣列類型。 - 回傳
-
*get()
.
T * operator->() const noexcept;
-
- 要求
-
T
不應為陣列類型。 - 回傳
-
get()
.
element_type & operator[](std::ptrdiff_t i) const noexcept;
-
- 要求
-
T
應該是陣列型別。儲存的指標不得為 0。i >= 0
。如果T
是U[N]
,則i < N
。 - 回傳
-
get()[i]
.
get
element_type * get() const noexcept;
-
- 回傳
-
儲存的指標。
local_use_count
long local_use_count() const noexcept;
-
- 回傳
-
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;
-
- 回傳
-
當且僅當
*this
和r
共用所有權或兩者皆為空時為true
。
自由函式
比較
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator==(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 回傳
-
a.get() == b.get()
.
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(local_shared_ptr<T> const & a, shared_ptr<U> const & b) noexcept;
template<class T, class U>
bool operator!=(shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 回傳
-
a.get() != b.get()
.
template<class T> bool operator==(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator==(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
-
- 回傳
-
p.get() == 0
.
template<class T> bool operator!=(local_shared_ptr<T> const & p, std::nullptr_t) noexcept;
template<class T> bool operator!=(std::nullptr_t, local_shared_ptr<T> const & p) noexcept;
-
- 回傳
-
p.get() != 0
.
template<class T, class U>
bool operator<(local_shared_ptr<T> const & a, local_shared_ptr<U> const & b) noexcept;
-
- 回傳
-
未指定的值,使得
-
operator<
是 C++ 標準 [lib.alg.sorting] 節中所述的嚴格弱排序; -
在
operator<
定義的等價關係下,!(a < b) && !(b < a)
,兩個local_shared_ptr
實例等價,若且唯若它們共用所有權或都為空。
-
注意
|
允許將 local_shared_ptr 物件用作關聯容器中的鍵。 |
注意
|
其餘的比較運算子已省略,因為這是設計。 |
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_shared
和 allocate_local_shared
提供了方便、安全且有效的方式來建立 local_shared_ptr
物件。它們類似於 shared_ptr
的 make_shared
和 allocate_shared
。
概要
make_local_shared
和 allocate_local_shared
定義於 <boost/smart_ptr/make_local_shared.hpp>
。
namespace boost {
// T is not an array
template<class T, class... Args>
local_shared_ptr<T> make_local_shared(Args&&... args);
template<class T, class A, class... Args>
local_shared_ptr<T> allocate_local_shared(const A& a, Args&&... args);
// T is an array of unknown bounds
template<class T>
local_shared_ptr<T> make_local_shared(std::size_t n);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n);
// T is an array of known bounds
template<class T>
local_shared_ptr<T> make_local_shared();
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a);
// T is an array of unknown bounds
template<class T>
local_shared_ptr<T> make_local_shared(std::size_t n,
const remove_extent_t<T>& v);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a, std::size_t n,
const remove_extent_t<T>& v);
// T is an array of known bounds
template<class T>
local_shared_ptr<T> make_local_shared(const remove_extent_t<T>& v);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared(const A& a,
const remove_extent_t<T>& v);
// T is not an array of known bounds
template<class T>
local_shared_ptr<T> make_local_shared_noinit();
template<class T, class A>
local_shared_ptr<T> allocate_local_shared_noinit(const A& a);
// T is an array of unknown bounds
template<class T>
local_shared_ptr<T> make_local_shared_noinit(std::size_t n);
template<class T, class A>
local_shared_ptr<T> allocate_local_shared_noinit(const A& a,
std::size_t n);
}
描述
這些函式的需求和效果與 make_shared
和 allocate_shared
相同,只是傳回 local_shared_ptr
。
通用指標轉換
描述
指標轉換函式樣板 (static_pointer_cast
、dynamic_pointer_cast
、const_pointer_cast
和 reinterpret_pointer_cast
) 提供了一種為原始指標、std::shared_ptr
和 std::unique_ptr
撰寫泛型指標轉換的方式。
在 pointer_cast_test.cpp 中有測試和範例程式碼
原理
Boost 智慧指標通常會多載這些函式,以提供模擬指標轉換的機制。例如,shared_ptr<T>
以這種方式實作靜態指標轉換
template<class T, class U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U>& p);
指標轉換函式樣板是原始指標、std::shared_ptr
和 std::unique_ptr
的 static_pointer_cast
、dynamic_pointer_cast
、const_pointer_cast
和 reinterpret_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_ptr
的 boost::hash
和 std::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);
}
這裡需要注意的關鍵是編譯器產生的複製建構子、指派運算子和解構子都具有合理的含義。因此,file
是 CopyConstructible
和 Assignable
,允許在標準容器中使用。
使用抽象類別來隱藏實作
另一種廣泛使用的 C++ 慣用語用於分離介面和實作是使用抽象基底類別和工廠函式。抽象類別有時稱為「介面」,而此模式稱為「基於介面的程式設計」。同樣,shared_ptr
可以用作工廠函式的傳回型別
// X.hpp:
class X
{
public:
virtual void f() = 0;
virtual void g() = 0;
protected:
~X() {}
};
shared_ptr<X> createX();
// X.cpp:
class X_impl: public X
{
private:
X_impl(X_impl const &);
X_impl & operator=(X_impl const &);
public:
virtual void f()
{
// ...
}
virtual void g()
{
// ...
}
};
shared_ptr<X> createX()
{
shared_ptr<X> px(new X_impl);
return px;
}
shared_ptr
的一個關鍵屬性是配置、建構、解除配置和銷毀詳細資訊是在建構時(在工廠函式內部)擷取的。
請注意上面範例中的受保護和非虛擬解構子。用戶端程式碼不能也不需要刪除指向 X
的指標;從 createX
傳回的 shared_ptr<X>
實例將正確呼叫 ~X_impl
。
防止 delete px.get()
通常希望防止用戶端程式碼刪除由 shared_ptr
管理的指標。先前的技術展示了一種可能的方法,即使用受保護的解構子。另一種替代方法是使用私有刪除器
class X
{
private:
~X();
class deleter;
friend class deleter;
class deleter
{
public:
void operator()(X * p) { delete p; }
};
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X, X::deleter());
return px;
}
};
封裝配置詳細資料,包裝工廠函式
shared_ptr
可用於建立 C++ 包裝函式,以封裝從工廠函式傳回原始指標的現有 C 樣式程式庫介面,以封裝配置詳細資訊。例如,考慮以下介面,其中 CreateX
可能會從其自己的私有堆積配置 X
,~X
可能無法存取,或者 X
可能不完整
X * CreateX(); void DestroyX(X *);
可靠銷毀 CreateX
傳回的指標的唯一方法是呼叫 DestroyX
。
以下是基於 shared_ptr
的包裝函式的樣子
shared_ptr<X> createX() { shared_ptr<X> px(CreateX(), DestroyX); return px; }
呼叫 createX
的用戶端程式碼仍然不需要知道物件是如何配置的,但現在銷毀是自動的。
使用 shared_ptr 來保存靜態配置物件的指標
有時希望建立指向現有物件的 shared_ptr
,以便當不再有任何參考時,shared_ptr
不會嘗試銷毀該物件。例如,工廠函式
shared_ptr<X> createX();
在某些情況下可能需要傳回指向靜態配置的 X
實例的指標。
解決方案是使用什麼都不做的自訂刪除器
struct null_deleter
{
void operator()(void const *) const
{
}
};
static X x;
shared_ptr<X> createX()
{
shared_ptr<X> px(&x, null_deleter());
return px;
}
相同的技術適用於任何已知比指標壽命更長的物件。
使用 shared_ptr 來保存 COM 物件的指標
背景:COM 物件具有內嵌的參考計數和兩個操作它的成員函式。AddRef()
會遞增計數。當計數降至零時,Release()
會遞減計數並自行銷毀。
可以將指向 COM 物件的指標保留在 shared_ptr
中
shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p) { p->AddRef(); shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release)); return pw; }
但是,請注意從 pw
建立的 shared_ptr
副本不會在 COM 物件的內嵌計數中「註冊」;它們將共用在 make_shared_from_COM
中建立的單個參考。當最後一個 shared_ptr
被銷毀時,從 pw
建立的弱指標將失效,而無論 COM 物件本身是否仍然存活。
如同在 mem_fn
文件中 解釋 的,您需要先 #define BOOST_MEM_FN_ENABLE_STDCALL
。
使用 shared_ptr 來保存具有內嵌參考計數的物件指標
這是上述技術的推廣。該範例假設物件實作了 intrusive_ptr
所需的兩個函式 intrusive_ptr_add_ref
和 intrusive_ptr_release
template<class T> struct intrusive_deleter
{
void operator()(T * p)
{
if(p) intrusive_ptr_release(p);
}
};
shared_ptr<X> make_shared_from_intrusive(X * p)
{
if(p) intrusive_ptr_add_ref(p);
shared_ptr<X> px(p, intrusive_deleter<X>());
return px;
}
使用 shared_ptr 來保存另一個共享擁有權智慧指標
shared_ptr
的設計目標之一是在程式庫介面中使用。可能會遇到程式庫採用 shared_ptr
引數的情況,但手頭的物件正在由不同的參考計數或連結智慧指標管理。
可以利用 shared_ptr
的自訂刪除器功能,將此現有的智慧指標包裝在 shared_ptr
外觀後面
template<class P> struct smart_pointer_deleter
{
private:
P p_;
public:
smart_pointer_deleter(P const & p): p_(p)
{
}
void operator()(void const *)
{
p_.reset();
}
P const & get() const
{
return p_;
}
};
shared_ptr<X> make_shared_from_another(another_ptr<X> qx)
{
shared_ptr<X> px(qx.get(), smart_pointer_deleter< another_ptr<X> >(qx));
return px;
}
一個微妙的點是刪除器不允許擲回例外,並且上述範例假設 p_.reset()
不會擲回。如果不是這種情況,則應將 p_.reset();
包裝在忽略例外的 try {} catch(…) {}
區塊中。在擲回並忽略例外(通常不太可能)的情況下,當刪除器的生命週期結束時,p_
將被釋放。當所有參考(包括弱指標)被銷毀或重設時,就會發生這種情況。
另一個曲折是,給定上述 shared_ptr
實例,可以使用 get_deleter
恢復原始的智慧指標
void extract_another_from_shared(shared_ptr<X> px)
{
typedef smart_pointer_deleter< another_ptr<X> > deleter;
if(deleter const * pd = get_deleter<deleter>(px))
{
another_ptr<X> qx = pd->get();
}
else
{
// not one of ours
}
}
從原始指標取得 shared_ptr
有時需要獲取 shared_ptr
給定一個原始指標,該指標指向另一個 shared_ptr
實例已經管理的物件。範例
void f(X * p) { shared_ptr<X> px(???); }
在 f
內部,我們想要建立指向 *p
的 shared_ptr
。
在一般情況下,此問題無解。一種方法是修改 f
以接受 shared_ptr
(如果可能)
void f(shared_ptr<X> px);
相同的轉換可以用於非虛擬成員函式,以轉換隱含的 this
void X::f(int m);
將變成具有 shared_ptr
第一個引數的自由函式
void f(shared_ptr<X> this_, int m);
如果無法變更 f
,但 X
使用內嵌計數,則使用上述 make_shared_from_intrusive。或者,如果已知在 f
中建立的 shared_ptr
永遠不會比物件壽命更長,則使用空刪除器。
在建構子中取得指向此物件的 shared_ptr (weak_ptr)
某些設計要求物件在建構時向中央授權機構註冊自己。當註冊例程接受 shared_ptr
時,這會導致問題:建構子如何取得指向 this
的 shared_ptr
class X
{
public:
X()
{
shared_ptr<X> this_(???);
}
};
在一般情況下,此問題無法解決。正在建構的 X
實例可以是自動變數或靜態變數;它可以在堆積上建立
shared_ptr<X> px(new X);
但在建構時,px
尚未存在,並且無法建立另一個與它共用所有權的 shared_ptr
實例。
根據上下文,如果內部 shared_ptr this_
不需要使物件保持存活,請如 此處 和 此處 所述,使用 null_deleter
。如果 X
應該始終存在於堆積上,並由 shared_ptr
管理,請使用靜態工廠函式
class X
{
private:
X() { ... }
public:
static shared_ptr<X> create()
{
shared_ptr<X> px(new X);
// use px as 'this_'
return px;
}
};
取得指向此物件的 shared_ptr
有時需要在虛擬成員函式中從 this
取得 shared_ptr
,並假設 this
已經由 shared_ptr
管理。無法套用先前技術中描述的轉換。
一個典型的範例
class X
{
public:
virtual void f() = 0;
protected:
~X() {}
};
class Y
{
public:
virtual shared_ptr<X> getX() = 0;
protected:
~Y() {}
};
// --
class impl: public X, public Y
{
public:
impl() { ... }
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
shared_ptr<X> px(???);
return px;
}
};
解決方案是將指向 this
的弱指標作為 impl
的成員保留
class impl: public X, public Y
{
private:
weak_ptr<impl> weak_this;
impl(impl const &);
impl & operator=(impl const &);
impl() { ... }
public:
static shared_ptr<impl> create()
{
shared_ptr<impl> pi(new impl);
pi->weak_this = pi;
return pi;
}
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
shared_ptr<X> px(weak_this);
return px;
}
};
此函式庫現在包含一個輔助類別模板 enable_shared_from_this
,可用於封裝解決方案。
class impl: public X, public Y, public enable_shared_from_this<impl>
{
public:
impl(impl const &);
impl & operator=(impl const &);
public:
virtual void f() { ... }
virtual shared_ptr<X> getX()
{
return shared_from_this();
}
}
請注意,您不再需要在 enable_shared_from_this
中手動初始化 weak_ptr
成員。建構 impl
的 shared_ptr
會處理此問題。
使用 shared_ptr 作為智慧計數句柄
某些函式庫介面使用不透明的句柄,這是上述 不完整類別技術 的一種變體。例如:
typedef void * HANDLE;
HANDLE CreateProcess();
void CloseHandle(HANDLE);
除了原始指標,也可以使用 shared_ptr
作為句柄,並免費獲得參考計數和自動資源管理。
typedef shared_ptr<void> handle;
handle createProcess()
{
shared_ptr<void> pv(CreateProcess(), CloseHandle);
return pv;
}
使用 shared_ptr 在區塊結束時執行程式碼
當控制流程離開作用域時,shared_ptr<void>
可以自動執行清理程式碼。
-
執行
f(p)
,其中p
是一個指標。shared_ptr<void> guard(p, f);
-
執行任意程式碼:
f(x, y)
shared_ptr<void> guard(static_cast<void*>(0), bind(f, x, y));
使用 shared_ptr<void> 來保存任意物件
shared_ptr<void>
可以作為類似 void*
的通用物件指標。當 shared_ptr<void>
實例建構為:
shared_ptr<void> pv(new X);
被銷毀時,它會正確地執行 ~X
來處置 X
物件。
此特性可以用與原始 void*
相同的方式來暫時從物件指標中移除型別資訊。之後可以使用 static_pointer_cast
將 shared_ptr<void>
轉換回正確的型別。
將任意資料與異質的 shared_ptr
實例關聯
shared_ptr
和 weak_ptr
支援標準關聯容器(如 std::map
)所需的 operator<
比較。這可以用於以非侵入式方式將任意資料與 shared_ptr
管理的物件相關聯。
typedef int Data;
std::map<shared_ptr<void>, Data> userData;
// or std::map<weak_ptr<void>, Data> userData; to not affect the lifetime
shared_ptr<X> px(new X);
shared_ptr<int> pi(new int(3));
userData[px] = 42;
userData[pi] = 91;
使用 shared_ptr
作為 CopyConstructible
互斥鎖
有時,有必要從函式返回互斥鎖,而不可複製的鎖不能以傳值方式返回。可以使用 shared_ptr
作為互斥鎖。
class mutex
{
public:
void lock();
void unlock();
};
shared_ptr<mutex> lock(mutex & m)
{
m.lock();
return shared_ptr<mutex>(&m, mem_fn(&mutex::unlock));
}
更好的是,作為鎖的 shared_ptr
實例可以封裝在專用的 shared_lock
類別中。
class shared_lock
{
private:
shared_ptr<void> pv;
public:
template<class Mutex> explicit shared_lock(Mutex & m): pv((m.lock(), &m), mem_fn(&Mutex::unlock)) {}
};
現在可以將 shared_lock
用作:
shared_lock lock(m);
請注意,由於 shared_ptr<void>
隱藏型別資訊的能力,shared_lock
並不以互斥鎖型別作為模板參數。
使用 shared_ptr 來包裝成員函式呼叫
shared_ptr
實現了 Bjarne Stroustrup 在文章「包裝 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_ptr
和 counted_ptr
的類別,它們非常類似於我們現在稱之為 scoped_ptr
和 shared_ptr
的類別。在少數幾個圖書館工作組的建議沒有被全委員會採納的情況下,counted_ptr
被拒絕,並且令人驚訝的擁有權轉移語義被添加到 auto_ptr
。
1998 年 10 月
Beman Dawes 提議以 safe_ptr
和 counted_ptr
的名稱恢復原始語義,並與 Per Andersson、Matt Austern、Greg Colvin、Sean Corfield、Pete Becker、Nico Josuttis、Dietmar Kühl、Nathan Myers、Chichiang Wan 和 Judy Ward 會面。在討論過程中,確定了四個新的類別名稱,決定沒有必要完全遵循 std::auto_ptr
介面,並確定了各種函式簽名和語義。
在接下來的三個月中,考慮了 shared_ptr
的幾種實作方式,並在 boost.org 郵件列表中進行了討論。實作問題圍繞著必須保留的參考計數,可以附加到指向的物件,也可以在其他地方分離。這些變體本身都有兩種主要的變體:
-
直接分離:
shared_ptr
包含一個指向物件的指標和一個指向計數的指標。 -
間接分離:
shared_ptr
包含一個指向輔助物件的指標,該輔助物件又包含一個指向物件和計數的指標。 -
嵌入式附加:計數是指向物件的成員。
-
位置附加:計數是通過運算符 new 操作附加的。
每種實作技術都有優缺點。我們甚至對直接和間接方法進行了各種計時,發現至少在 Intel Pentium 晶片上,幾乎沒有可測量的差異。Kevlin Henney 提供了一篇他寫的關於「計數主體技術」的論文。Dietmar Kühl 提出了一種優雅的部分範本特殊化技術,允許使用者選擇他們喜歡的實作方式,並且也進行了實驗。
但 Greg Colvin 和 Jerry Schwarz 認為「參數化會讓使用者望而卻步」,最終我們選擇只提供直接實作。
1999 年 5 月
在 1999 年的 4 月和 5 月,Valentin Bonnard 和 David Abrahams 提出了許多建議,從而實現了許多改進。
1999 年 9 月
Luis Coelho 提供了 shared_ptr::swap
和 shared_array::swap
。
1999 年 11 月
Darin Adler 提供了共享型別的 operator ==
、operator !=
以及 std::swap
和 std::less
特殊化。
2001 年 5 月
Vladimir Prus 建議在銷毀時需要完整的型別。改進是在 Dave Abrahams、Greg Colvin、Beman Dawes、Rainer Deyke、Peter Dimov、John Maddock、Vladimir Prus、Shankar Sai 等人的討論中發展起來的。
2002 年 1 月
Peter Dimov 重做了所有四個類別,增加了功能,修復了錯誤,將它們分成四個單獨的標頭,並添加了 weak_ptr
。
2003 年 3 月
Peter Dimov、Beman Dawes 和 Greg Colvin 提議將 shared_ptr
和 weak_ptr
透過第一個函式庫技術報告(稱為 TR1)納入標準函式庫。該提案被接受,並最終成為 2011 年 C++ 標準的一部分。
2007 年 7 月
Peter Dimov 和 Beman Dawes 提議對 shared_ptr
進行一些增強,因為它正在進入最終成為 C++11 標準的工作草案中。
2012 年 11 月
Glen Fernandes 提供了陣列的 make_shared
和 allocate_shared
的實作。它們為可以使用建構函式參數或初始值設定項列表初始化的陣列實現了單一分配,以及用於預設初始化和無值初始化的多載。
Peter Dimov 通過語法 shared_ptr<T[]>
和 shared_ptr<T[N]>
擴展了 shared_ptr
來支援陣列,從而幫助了這一開發。
2013 年 4 月
Peter Dimov 提議擴展 shared_ptr
以支援陣列,以納入標準中,並且已被接受。
2014 年 2 月
Glen Fernandes 更新了 make_shared
和 allocate_shared
以符合 C++ 標準論文 N3870 中的規範,並為陣列和物件實作了 make_unique
。
Peter Dimov 和 Glen Fernandes 分別更新了純量和陣列實作,以解決 C++ 標準函式庫缺陷 2070。
2017 年 2 月
Glen Fernandes 為陣列重寫了 allocate_shared
和 make_shared
,以實現更優化和更易於維護的實作。
2017 年 6 月
Peter Dimov 和 Glen Fernandes 以 Asciidoc 格式重寫了文件。
Peter Dimov 添加了 atomic_shared_ptr
和 local_shared_ptr
。
2019 年 8 月
Glen Fernandes 實作了純量和陣列的 allocate_unique
。
附錄 C:shared_array (已棄用)
注意
|
此功能已棄用,因為現在可以使用指向 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::vector
的 shared_ptr
是 shared_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
,儲存p
和d
的副本。之後,使用計數為 1。當需要刪除p
指向的陣列時,物件d
用於語句d(p)
。 - 要求
-
T
是一個完整的型別。 -
D
的複製建構函式和解構函式不得拋出例外。 -
使用參數
p
呼叫物件d
不得拋出例外。- 擲回
-
std::bad_alloc
。如果拋出例外,則會呼叫d(p)
。
shared_array(const shared_array& v) noexcept;
-
- 效果
-
建構
shared_array
,如同儲存v
中儲存的指標的副本。之後,所有副本的使用計數都比初始使用計數多 1。 - 要求
-
T
是一個完整的型別。
解構子
~shared_array() noexcept;
-
- 效果
-
遞減使用計數。然後,如果使用計數為 0,則會刪除儲存的指標指向的陣列。請注意,對值為 0 的指標使用
delete[]
是無害的。
指派
shared_array& operator=(const shared_array& v) noexcept;
-
- 效果
-
如上所述建構新的
shared_array
,然後用新的shared_array
替換此shared_array
,並銷毀被替換的物件。 - 要求
-
T
是一個完整的型別。 - 回傳
-
*this
.
reset
void reset(T* p = 0);
-
- 效果
-
如上所述建構新的
shared_array
,然後用新的shared_array
替換此shared_array
,並銷毀被替換的物件。 - 要求
-
T
是一個完整的型別。 - 擲回
-
std::bad_alloc
。如果拋出例外,則會呼叫delete[] p
。
template<class D> void reset(T* p, D d);
-
- 效果
-
如上所述建構新的
shared_array
,然後用新的shared_array
替換此shared_array
,並銷毀被替換的物件。 - 要求
-
T
是一個完整的型別。 -
D
的複製建構函式不得拋出例外。- 擲回
-
std::bad_alloc
。如果拋出例外,則會呼叫d(p)
。
索引
T& operator[](std::ptrdiff_t n) const noexcept;
- 回傳
-
儲存的指標所指向的陣列的元素
n
的參考。如果儲存的指標為 0,或者如果n
小於 0 或大於或等於陣列中的元素數,則行為未定義且幾乎可以肯定是不希望的。 - 要求
-
T
是一個完整的型別。
get
T* get() const noexcept;
-
- 回傳
-
儲存的指標。
unique
bool unique() const noexcept;
-
- 回傳
-
如果沒有其他
shared_array
共用儲存的指標的所有權,則為true
;否則為false
。
use_count
long use_count() const noexcept;
-
- 回傳
-
共用儲存的指標所有權的
shared_array
物件的數量。
轉換
explicit operator bool() const noexcept;
-
- 回傳
-
get() != 0
. - 要求
-
T
是一個完整的型別。
swap
void swap(shared_array<T>& b) noexcept;
-
- 效果
-
交換兩個智慧型指標的內容。
自由函式
比較
template<class T> bool
operator==(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator!=(const shared_array<T>& a, const shared_array<T>& b) noexcept;
template<class T> bool
operator<(const shared_array<T>& a, const shared_array<T>& b) noexcept;
-
- 回傳
-
比較兩個智能指標的儲存指標的結果。
注意
|
提供 operator< 多載以定義順序,以便 shared_array 物件可以在關聯容器(如 std::map )中使用。該實作使用 std::less<T*> 執行比較。這確保了正確處理比較,因為標準規定指標的關係運算未指定(5.9 [expr.rel] 段落 2),但指標的 std::less 已明確定義(20.3.3 [lib.comparisons] 段落 8)。 |
swap
template<class T>
void swap(shared_array<T>& a, shared_array<T>& b) noexcept;
-
- 回傳
-
a.swap(b)
. - 要求
-
T
是一個完整的型別。
附錄 D:版權和授權
此文件是
-
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 發布。