共享所有權(stdshared ptr)
類别範本 std::shared_ptr
定義了一個共享指標,該指標能夠與其他共享指標共享物件的所有權。這與代表獨家所有權的 std::unique_ptr
形成對比。
共享行為通過稱為引用計數的技術實現,其中指向物件的共享指標的數量與其一起儲存。當此計數達到零時,無論是通過銷燬還是重新分配最後一個 std::shared_ptr
例項,物件都會自動銷燬。
// Creation: 'firstShared' is a shared pointer for a new instance of 'Foo'
std::shared_ptr<Foo> firstShared = std::make_shared<Foo>(/*args*/);
要建立共享同一物件的多個智慧指標,我們需要建立另一個使第一個共享指標別名的 shared_ptr
。這有兩種方法:
std::shared_ptr<Foo> secondShared(firstShared); // 1st way: Copy constructing
std::shared_ptr<Foo> secondShared;
secondShared = firstShared; // 2nd way: Assigning
上述兩種方法都使得 secondShared
成為共享指標,與 firstShared
共享我們的 Foo
例項的所有權。
智慧指標就像原始指標一樣工作。這意味著,你可以使用*
取消引用它們。常規的 ->
運算子也可以執行:
secondShared->test(); // Calls Foo::test()
最後,當最後一個別名 shared_ptr
超出範圍時,我們的 Foo
例項的解構函式被呼叫。
警告: 當需要分配共享所有權語義的額外資料時,構造 shared_ptr
可能會丟擲 bad_alloc
異常。如果建構函式傳遞了常規指標,則它假定擁有指向的物件,並在丟擲異常時呼叫刪除器。這意味著如果 shared_ptr<T>
的分配失敗,shared_ptr<T>(new T(args))
將不會洩漏 T
物件。但是,建議使用 make_shared<T>(args)
或 allocate_shared<T>(alloc, args)
,這使得實現能夠優化記憶體分配。
使用 shared_ptr 分配陣列([])
Version < C++ 17
不幸的是,沒有直接的方法來使用 make_shared<>
分配陣列。
可以使用 new
和 std::default_delete
為 shared_ptr<>
建立陣列。
例如,要分配 10 個整數的陣列,我們可以將程式碼編寫為
shared_ptr<int> sh(new int[10], std::default_delete<int[]>());
此處必須指定 std::default_delete
以確保使用 delete[]
正確清除分配的記憶體。
如果我們在編譯時知道大小,我們可以這樣做:
template<class Arr>
struct shared_array_maker {};
template<class T, std::size_t N>
struct shared_array_maker<T[N]> {
std::shared_ptr<T> operator()const{
auto r = std::make_shared<std::array<T,N>>();
if (!r) return {};
return {r.data(), r};
}
};
template<class Arr>
auto make_shared_array()
-> decltype( shared_array_maker<Arr>{}() )
{ return shared_array_maker<Arr>{}(); }
然後 make_shared_array<int[10]>
返回一個指向 10 個整數的 shared_ptr<int>
,所有預設構造。
Version >= C++ 17
使用 C++ 17,shared_ptr
獲得了對陣列型別的特殊支援 。不再需要顯式指定陣列刪除器,並且可以使用 []
陣列索引運算子取消引用共享指標:
std::shared_ptr<int[]> sh(new int[10]);
sh[0] = 42;
共享指標可以指向它擁有的物件的子物件:
struct Foo { int x; };
std::shared_ptr<Foo> p1 = std::make_shared<Foo>();
std::shared_ptr<int> p2(p1, &p1->x);
p2
和 p1
都擁有 Foo
型別的物件,但是 p2
指向它的 int
成員 x
。這意味著如果 p1
超出範圍或被重新分配,底層的 Foo
物件仍將存活,確保 p2
不會懸掛。
重要提示: shared_ptr
只知道自己和使用別名建構函式建立的所有其他 shared_ptr
。它不知道任何其他指標,包括通過引用同一 Foo
例項建立的所有其他 shared_ptr
:
Foo *foo = new Foo;
std::shared_ptr<Foo> shared1(foo);
std::shared_ptr<Foo> shared2(foo); // don't do this
shared1.reset(); // this will delete foo, since shared1
// was the only shared_ptr that owned it
shared2->test(); // UNDEFINED BEHAVIOR: shared2's foo has been
// deleted already!!
所有權轉讓 shared_ptr
預設情況下,shared_ptr
會增加引用計數,並且不會轉移所有權。但是,可以使用 std::move
轉移所有權:
shared_ptr<int> up = make_shared<int>();
// Transferring the ownership
shared_ptr<int> up2 = move(up);
// At this point, the reference count of up = 0 and the
// ownership of the pointer is solely with up2 with reference count = 1