簡單總結族操,詳見參考資料肝匆。
引用計數(shù)的實現(xiàn)方式
引用計數(shù)是與對象綁定的坛悉,并且可能有多個shared_ptr綁定同一對象,為了達到同時更新多個shared_ptr的引用計數(shù)台颠,所以需要讓他們指向同一個引用計數(shù)褐望,所以只能在堆上另外分配空間來存放引用計數(shù)。
為了保證多線程讀同一個shared_ptr是安全的串前,引用計數(shù)的增減是原子操作瘫里。
線程安全性
shared_ptr 的線程安全級別和內建類型、標準庫容器荡碾、std::string 一樣谨读,即:
一個 shared_ptr 對象實體可被多個線程同時讀取坛吁;
兩個 shared_ptr 對象實體可以被兩個線程同時寫入劳殖,“析構”算寫操作铐尚;
如果要從多個線程讀寫同一個 shared_ptr 對象,那么需要加鎖哆姻;
為什么shared_ptr不能并發(fā)讀寫:因為 shared_ptr 有兩個數(shù)據(jù)成員(引用計數(shù)宣增、對象指針),讀寫操作不能原子化矛缨。
具體發(fā)生多線程問題的場景可以自己推導爹脾,更詳細的說明可以看陳碩寫的文章:
為什么多線程讀寫 shared_ptr 要加鎖?
enable_shared_from_this的實現(xiàn)
一個主要的場景是保證異步回調函數(shù)中操作的對象仍然有效箕昭。
std::enable_shared_from_this<T> 有一個std::weak_ptr<T>的成員灵妨,實際上在構造std::enable_shared_from_this<T>時,并沒有初始化std::weak_ptr<T>成員落竹,而是在用這個std::enable_shared_from_this<T>去構造std::shared_ptr的時候泌霍,去構造并初始化這個std::weak_ptr<T>成員。所以這也就是為什么cppreference中說的這個對象必須是std::shared_ptr管理的述召,因為這個對象不是通過std::shared_ptr來管理烹吵,那么std::weak_ptr是未初始化的,無法通過其提升為std::shared_ptr對象桨武。
如何判斷是否繼承了shared_from_this?模板類型推導:根據(jù)是否有繼承特定類型實例化不同的模板實現(xiàn)肋拔;
多繼承場景下使用虛繼承解決問題;
make_shared/new
與直接使用new相比呀酸,make函數(shù)不存在代碼重復且具備異常安全性凉蜂,std::make_shared和std::allocate_shared可以生成更快更短的目標碼。
對象和控制塊分配在一塊內存上性誉,減少了內存分配的次數(shù)窿吩。但是這并不一定是好事,因為這導致對象和控制塊占用的內存也要一次回收掉错览。即纫雁,如果還有std::weak_ptr存在,控制塊就要在倾哺,對象占用的內存也沒辦法回收轧邪。如果對象比較大,且std::weak_ptr在對象析構后還可能長期存在羞海,那么這種開銷是不可忽視的忌愚。
在需要自定義刪除器及以大括號初始化時make函數(shù)無能為力。
更詳細可以閱讀《Effective Moden C++》Item 21却邓。
pointer_cast的實現(xiàn)
智能指針的類型轉換無法通過static_cast等實現(xiàn)硕糊,需要使用static_pointer_cast方法(想一下如果取裸指針出來static_cast會發(fā)生什么)。static_pointer_cast是通過shared_ptr一個特殊的構造函數(shù)實現(xiàn)的。
template< class T, class U >
std::shared_ptr<T> static_pointer_cast( const std::shared_ptr<U>& r ) noexcept
{
auto p = static_cast<typename std::shared_ptr<T>::element_type*>(r.get());
return std::shared_ptr<T>(r, p);
}
shared_ptr與deleter
shared_ptr<Foo> sp(new Foo)在構造sp的時候捕獲了Foo的析構行為简十,意味著:
-
shared_ptr<void> 可以指向并安全地管理(析構或防止析構)任何對象檬某,派生類沒有虛析構函數(shù)也能被正確析構
shared_ptr<Foo> sp1(new Foo); // ref_count.ptr 的類型是 Foo*
shared_ptr<void> sp2 = sp1; // 可以賦值,F(xiàn)oo* 向 void* 自動轉型
sp1.reset(); // 這時 Foo 對象的引用計數(shù)降為 1
此后 sp2 仍然能安全地管理 Foo 對象的生命期螟蝙,并安全完整地釋放 Foo恢恼,不會出現(xiàn) delete void* 的情況,因為 delete 的是 ref_count.ptr胶逢,不是 sp2.ptr。
std::shared_ptr<void>的工作原理
- shared_ptr進行資源銷毀時饰潜,總會調用創(chuàng)建智能指針的那個DLL中的delete初坠,這意味著shared_ptr可以隨意地在DLL間傳遞而不需擔心跨DLL的問題。(Effective C++ Item18)
參考資料:
為什么多線程讀寫 shared_ptr 要加鎖
std::shared_ptr<void>的工作原理
《Effective C++》
《Effective Modern C++》