std::shared_ptr
定義於標頭檔案 <memory> |
||
template< class T > class shared_ptr; |
(C++11 起) | |
std::shared_ptr
是一個智慧指標,它透過指標共享物件的所有權。多個 shared_ptr
物件可以擁有同一個物件。當以下任一情況發生時,物件會被銷燬,其記憶體會被釋放:
物件透過 delete-expression 或在構造 shared_ptr
時提供的自定義刪除器進行銷燬。
一個 shared_ptr
可以共享一個物件的所有權,同時儲存指向另一個物件的指標。此功能可用於指向成員物件,同時擁有它們所屬的物件。儲存的指標是 get()、解引用和比較運算子所訪問的指標。當引用計數為零時,傳遞給刪除器的指標是受管理指標。
一個 shared_ptr
也可以不擁有任何物件,在這種情況下它被稱為 *空* (如果使用別名建構函式建立空 shared_ptr
,它可能有一個非空的儲存指標)。
shared_ptr
的所有特化都滿足 CopyConstructible、CopyAssignable 和 LessThanComparable 的要求,並且可以 上下文轉換為 bool
。
所有成員函式(包括複製建構函式和複製賦值)可以在不同 shared_ptr
物件上被多個執行緒呼叫,而無需額外同步,即使這些物件是副本並共享同一物件的所有權。如果多個執行執行緒在沒有同步的情況下訪問同一個 shared_ptr
物件,並且其中任何一個訪問使用了 shared_ptr
的非 const 成員函式,則會發生資料競爭;可以使用 std::atomic
目錄 |
[編輯] 成員型別
成員型別 | 定義 | ||||
element_type
|
| ||||
weak_type (從 C++17 開始) |
std::weak_ptr<T> |
[編輯] 成員函式
構造新的 shared_ptr (公共成員函式) | |
如果沒有更多的 shared_ptr 連結到它,則銷燬所擁有的物件。(公共成員函式) | |
賦值 shared_ptr (公共成員函式) | |
修改器 | |
替換託管物件 (公共成員函式) | |
交換管理物件 (公共成員函式) | |
觀察器 | |
返回儲存的指標 (公共成員函式) | |
解引用儲存的指標 (公共成員函式) | |
(C++17) |
提供對儲存陣列的索引訪問 (公共成員函式) |
返回引用同一管理物件的 shared_ptr 物件的數量(公共成員函式) | |
(C++20 前) |
檢查管理物件是否僅由當前的 shared_ptr 物件管理(公共成員函式) |
檢查儲存的指標是否不為空 (公共成員函式) | |
提供共享指標的基於所有權的排序 (公共成員函式) | |
(C++26) |
提供基於所有者的共享指標雜湊 (公共成員函式) |
(C++26) |
提供基於所有者的共享指標相等比較 (公共成員函式) |
[編輯] 非成員函式
(C++20 起) |
建立一個管理新物件的共享指標 (函式模板) |
建立一個共享指標,該指標管理使用分配器分配的新物件 (函式模板) | |
對儲存的指標應用 static_cast、dynamic_cast、const_cast 或 reinterpret_cast (函式模板) | |
如果擁有,則返回指定型別的刪除器 (函式模板) | |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20) |
與另一個 shared_ptr 或 nullptr 比較(函式模板) |
將儲存的指標值輸出到輸出流 (函式模板) | |
(C++11) |
特化 std::swap 演算法 (函式模板) |
特化 std::shared_ptr 的原子操作(函式模板) |
[編輯] 輔助類
(C++20) |
原子共享指標 (類模板特化) |
(C++11) |
std::shared_ptr 的雜湊支援 (類模板特化) |
[編輯] 推導指南 (從 C++17 開始)
[編輯] 注意事項
一個物件的所有權只能透過複製構造或複製賦值其值給另一個 shared_ptr
來與另一個 shared_ptr
共享。使用由另一個 shared_ptr
擁有的原始底層指標構造新的 shared_ptr
會導致未定義行為。
std::shared_ptr
可以與 不完整型別 T
一起使用。但是,來自原始指標的建構函式(template<class Y> shared_ptr(Y*))和 template<class Y> void reset(Y*) 成員函式只能使用指向完整型別的指標呼叫(請注意,std::unique_ptr 可以由指向不完整型別的原始指標構造)。
std::shared_ptr<T> 中的 T
可以是函式型別:在這種情況下,它管理函式指標,而不是物件指標。這有時用於在任何函式被引用時保持動態庫或外掛載入。
void del(void(*)()) {} void fun() {} int main() { std::shared_ptr<void()> ee(fun, del); (*ee)(); }
[編輯] 實現說明
在典型的實現中,shared_ptr
只持有兩個指標
- 儲存的指標(由 get() 返回);
- 指向 *控制塊* 的指標。
控制塊是一個動態分配的物件,它包含
- 指向管理物件的指標或管理物件本身;
- 刪除器(型別擦除);
- 分配器(型別擦除);
- 擁有管理物件的
shared_ptr
數量; - 引用管理物件的
weak_ptr
數量。
當透過呼叫 std::make_shared 或 std::allocate_shared 建立 shared_ptr
時,控制塊和管理物件的記憶體都是透過單個分配建立的。管理物件在控制塊的資料成員中就地構造。當透過 shared_ptr
的一個建構函式建立 shared_ptr
時,管理物件和控制塊必須單獨分配。在這種情況下,控制塊儲存指向管理物件的指標。
由 shared_ptr
直接持有的指標是 get() 返回的指標,而由控制塊持有的指標/物件是當共享所有者數量達到零時將被刪除的指標/物件。這些指標不一定相等。
shared_ptr
的解構函式遞減控制塊的共享所有者數量。如果該計數器達到零,控制塊會呼叫管理物件的解構函式。控制塊本身不會被釋放,直到 std::weak_ptr 計數器也達到零。
在現有實現中,如果存在指向同一控制塊的共享指標,則弱指標的數量會增加([1],[2])。
為了滿足執行緒安全要求,引用計數器通常使用相當於 std::atomic::fetch_add 與 std::memory_order_relaxed 的操作來遞增(遞減需要更強的排序以安全地銷燬控制塊)。
[編輯] 示例
#include <chrono> #include <iostream> #include <memory> #include <mutex> #include <thread> using namespace std::chrono_literals; struct Base { Base() { std::cout << "Base::Base()\n"; } // Note: non-virtual destructor is OK here ~Base() { std::cout << "Base::~Base()\n"; } }; struct Derived : public Base { Derived() { std::cout << "Derived::Derived()\n"; } ~Derived() { std::cout << "Derived::~Derived()\n"; } }; void print(auto rem, std::shared_ptr<Base> const& sp) { std::cout << rem << "\n\tget() = " << sp.get() << ", use_count() = " << sp.use_count() << '\n'; } void thr(std::shared_ptr<Base> p) { std::this_thread::sleep_for(987ms); std::shared_ptr<Base> lp = p; // thread-safe, even though the // shared use_count is incremented { static std::mutex io_mutex; std::lock_guard<std::mutex> lk(io_mutex); print("Local pointer in a thread:", lp); } } int main() { std::shared_ptr<Base> p = std::make_shared<Derived>(); print("Created a shared Derived (as a pointer to Base)", p); std::thread t1{thr, p}, t2{thr, p}, t3{thr, p}; p.reset(); // release ownership from main print("Shared ownership between 3 threads and released ownership from main:", p); t1.join(); t2.join(); t3.join(); std::cout << "All threads completed, the last one deleted Derived.\n"; }
可能的輸出
Base::Base() Derived::Derived() Created a shared Derived (as a pointer to Base) get() = 0x118ac30, use_count() = 1 Shared ownership between 3 threads and released ownership from main: get() = 0, use_count() = 0 Local pointer in a thread: get() = 0x118ac30, use_count() = 5 Local pointer in a thread: get() = 0x118ac30, use_count() = 4 Local pointer in a thread: get() = 0x118ac30, use_count() = 2 Derived::~Derived() Base::~Base() All threads completed, the last one deleted Derived.
[編輯] 示例
#include <iostream> #include <memory> struct MyObj { MyObj() { std::cout << "MyObj constructed\n"; } ~MyObj() { std::cout << "MyObj destructed\n"; } }; struct Container : std::enable_shared_from_this<Container> // note: public inheritance { std::shared_ptr<MyObj> memberObj; void CreateMember() { memberObj = std::make_shared<MyObj>(); } std::shared_ptr<MyObj> GetAsMyObj() { // Use an alias shared ptr for member return std::shared_ptr<MyObj>(shared_from_this(), memberObj.get()); } }; #define COUT(str) std::cout << '\n' << str << '\n' #define DEMO(...) std::cout << #__VA_ARGS__ << " = " << __VA_ARGS__ << '\n' int main() { COUT("Creating shared container"); std::shared_ptr<Container> cont = std::make_shared<Container>(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creating member"); cont->CreateMember(); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Creating another shared container"); std::shared_ptr<Container> cont2 = cont; DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("GetAsMyObj"); std::shared_ptr<MyObj> myobj1 = cont->GetAsMyObj(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Copying alias obj"); std::shared_ptr<MyObj> myobj2 = myobj1; DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); DEMO(cont2.use_count()); DEMO(cont2->memberObj.use_count()); COUT("Resetting cont2"); cont2.reset(); DEMO(myobj1.use_count()); DEMO(myobj2.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Resetting myobj2"); myobj2.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); DEMO(cont->memberObj.use_count()); COUT("Resetting cont"); cont.reset(); DEMO(myobj1.use_count()); DEMO(cont.use_count()); }
輸出
Creating shared container cont.use_count() = 1 cont->memberObj.use_count() = 0 Creating member MyObj constructed cont.use_count() = 1 cont->memberObj.use_count() = 1 Creating another shared container cont.use_count() = 2 cont->memberObj.use_count() = 1 cont2.use_count() = 2 cont2->memberObj.use_count() = 1 GetAsMyObj myobj1.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 cont2.use_count() = 3 cont2->memberObj.use_count() = 1 Copying alias obj myobj1.use_count() = 4 myobj2.use_count() = 4 cont.use_count() = 4 cont->memberObj.use_count() = 1 cont2.use_count() = 4 cont2->memberObj.use_count() = 1 Resetting cont2 myobj1.use_count() = 3 myobj2.use_count() = 3 cont.use_count() = 3 cont->memberObj.use_count() = 1 Resetting myobj2 myobj1.use_count() = 2 cont.use_count() = 2 cont->memberObj.use_count() = 1 Resetting cont myobj1.use_count() = 1 cont.use_count() = 0 MyObj destructed
[編輯] 另請參閱
(C++11) |
具有唯一物件所有權語義的智慧指標 (類模板) |
(C++11) |
由 std::shared_ptr 管理的物件的弱引用 (類模板) |