C++智能指針shared_ptr與weak_ptr的實現(xiàn)分析
shared_ptr
采取引用計數(shù)來表示一塊內(nèi)存被幾個智能指針所共享,當引用計數(shù)為0時,會自動釋放該內(nèi)存,避免了忘記手動釋放造成的內(nèi)存泄露問題。采用引用計數(shù)來管理內(nèi)存對象的做法是Linux內(nèi)核慣用的手法。
weak_ptr
weak_ptr 設計的目的是為配合 shared_ptr 而引入的一種智能指針來協(xié)助 shared_ptr 工作, 它只可以從一個 shared_ptr 或另一個 weak_ptr 對象構造, 它的構造和析構不會引起引用記數(shù)的增加或減少。同時weak_ptr 沒有重載*和->,但可以使用 lock 獲得一個可用的 shared_ptr 對象(引用計數(shù)會增加1)。
內(nèi)存模型
RefCnt 和 Mdel實現(xiàn)
template<typename _Ty> class Mydeletor { public: Mydeletor() = default; void operator()(_Ty* p)const { if (p != NULL) { delete[]p; } p = NULL; } }; template<typename _Ty> class RefCnt { public: RefCnt(_Ty* p) :ptr(p), Uses(1), Weaks(0) { cout <<"RefCnt construct"<<endl; } ~RefCnt() {} void IncUses() { Uses += 1; } void IncWeaks() { Weaks += 1; } protected: _Ty* ptr; std::atomic_int Uses; std::atomic_int Weaks; friend class M_shared_ptr<_Ty>; friend class M_weak_ptr<_Ty>; };
shared_ptr 實現(xiàn)
template<typename _Ty,typename _De> class M_shared_ptr { private: _Ty* Ptr; RefCnt<_Ty>* Ref; _De mdeletor; public: M_shared_ptr(_Ty* p = nullptr) :Ptr(nullptr),Ref(nullptr) { if (p != nullptr) { Ptr = p; Ref = new RefCnt<_Ty>(p); } } M_shared_ptr(const M_shared_ptr& other):Ptr(other.Ptr),Ref(other.Ref)//拷貝構造 { if (Ptr != NULL) { Ref->IncUses(); } } M_shared_ptr(const M_weak_ptr<_Ty>& other):Ptr(other.GetRef()->ptr),Ref(other.GetRef())//用weak_ptr拷貝構造 { if (Ptr != NULL) { Ref->IncUses(); } } M_shared_ptr(M_shared_ptr&& other) :Ptr(other.Ptr), Ref(other.Ref)//移動構造 { other.Ptr = NULL; other.Ref = NULL; } M_shared_ptr& operator=(const M_shared_ptr& other)//賦值 { if (this == &other || Ptr == other.Ptr) return *this;//自賦值,直接返回本身 if (Ptr != NULL && --Ref->Uses == 0)//被賦值的智能指針對象擁有資源, { //且該對象僅被該智能指針擁有 mdeletor(Ptr);//釋放該對象 if (--Ref->Weaks == 0)//當弱引用計數(shù)為零時 { delete Ref;//析構引用計數(shù)對象 Ref = NULL; } } Ptr = other.Ptr; Ref = other.Ref; if (Ptr != NULL) { Ref->IncUses(); } return *this; } M_shared_ptr& operator=(M_shared_ptr&& other)//移動賦值 { if (this == &other) return *this; if (Ptr == other.Ptr && Ptr != NULL)//當兩個智能指針使用同一個對象時,且該對象不為空 { other.Ptr = NULL;//去掉other的使用權 other.Ref = NULL; Ref->Uses -= 1;//強引用計數(shù)-1 return *this; } if (Ptr != NULL && --Ref->Uses == 0) { mdeletor(Ptr); if (--Ref->Weaks == 0) { delete Ref; Ref = NULL; } } Ptr = other.Ptr; Ref = other.Ref; other.Ptr = NULL; other.Ref = NULL; return *this; } ~M_shared_ptr() { if (Ptr != NULL && --Ref->Uses == 0) { mdeletor(Ptr); if (--Ref->Weaks == 0) { delete Ref; } } Ref = NULL; } _Ty* get()const { return Ptr; } _Ty& operator*() { return *get(); } _Ty* operator->() { return get(); } size_t use_count()const { if (Ref == NULL) return 0; return Ref->Uses; } void swap(M_shared_ptr& other) { std::swap(Ptr, other.Ptr); std::swap(Ref, other.Ref); } operator bool()const { return Ptr != NULL; } friend class M_weak_ptr<_Ty>; };
weak_ptr 實現(xiàn)
template<typename _Ty> class M_weak_ptr { private: RefCnt<_Ty>* wRef; public: size_t use_count()const { if (wRef == NULL) return 0; return wRef->Uses; } size_t weak_count()const { if (wRef == NULL) return 0; return wRef->Weaks; } RefCnt<_Ty>* GetRef() const { return wRef; } M_weak_ptr() :wRef(NULL) {} M_weak_ptr(const M_shared_ptr<_Ty>& other) :wRef(other.Ref)//共享指針構造 { if (wRef!=NULL) { wRef->IncWeaks(); } } M_weak_ptr(const M_weak_ptr& other) :wRef(other.wRef)//拷貝構造 { if (wRef != NULL) { wRef->IncWeaks(); } } M_weak_ptr(M_weak_ptr&& other) :wRef(other.wRef)//移動構造 { other.wRef = NULL; } M_weak_ptr& operator=(const M_weak_ptr& other) { if (this == &other||wRef==other.wRef) return *this;//自賦值或者是兩個指針指向同一個對象 if (this != NULL && --wRef->Weaks == 0)//是否自己獨占對象 { delete wRef; } wRef = other.wRef; if (wRef != NULL) { wRef->IncUses(); } return *this; } M_weak_ptr& operator=(M_weak_ptr&& other) { //1 判斷是否自賦值 if (this == &other) return *this; //2 判斷是否是指向同一個對象的兩個指針相互賦值 if (wRef == other.wRef && wRef != NULL)//如果是 { other.wRef = NULL; wRef->Weaks -= 1; return *this; } //3 兩個指向不同對象的指針賦值 if (this != NULL && --wRef->Weaks == 0)//是否自己獨占對象 { delete wRef;//如果獨有 } wRef = other.wRef; other.wRef = NULL; return *this; } M_weak_ptr& operator=(const M_shared_ptr<_Ty>& other)//共享智能指針給弱指針賦值 { if (wRef == other.Ref) return *this; if (wRef != NULL && --wRef->Uses == 0) { delete wRef; } wRef = other.Ref; if (wRef != NULL) { wRef->IncWeaks(); } return *this; } M_weak_ptr& operator=( M_shared_ptr<_Ty>&& other) = delete; ~M_weak_ptr() { if (wRef != NULL && --wRef->Weaks == 0) { delete wRef; } wRef = NULL; } bool expired()const//判斷被引用的對象是否刪除,若刪除則返回真 { return wRef->Uses == 0; } M_shared_ptr<_Ty> lock()const { M_shared_ptr<_Ty> tmp; tmp.Ptr = wRef->ptr; tmp.Ref = wRef; tmp.Ref->IncUses(); return tmp; } };
shared_from_this()
std::enable_shared_from_this 能讓一個對象(假設其名為 t ,且已被一個 std::shared_ptr 對象 pt 管理)安全地生成其他額外的 std::shared_ptr 實例(假設名為 pt1, pt2, … ) ,它們與 pt 共享對象 t 的所有權。
使用原因:
1.把當前類對象作為參數(shù)傳給其他函數(shù)時,為什么要傳遞share_ptr呢?直接傳遞this指針不可以嗎?
一個裸指針傳遞給調(diào)用者,誰也不知道調(diào)用者會干什么?假如調(diào)用者delete了該對象,而share_tr此時還指向該對象。
2.這樣傳遞share_ptr可以嗎?share_ptr(this)
這樣會造成2個非共享的share_ptr指向一個對象,最后造成2次析構該對象。
class T需要繼承enable_shared_from_this才能使用shared_from_this(),具體源碼如下:
namespace boost { template<class T> class enable_shared_from_this { protected: enable_shared_from_this() { } enable_shared_from_this(enable_shared_from_this const &) { } enable_shared_from_this & operator=(enable_shared_from_this const &) { return *this; } ~enable_shared_from_this() { } public: shared_ptr<T> shared_from_this() { shared_ptr<T> p( weak_this_ ); BOOST_ASSERT( p.get() == this ); return p; } shared_ptr<T const> shared_from_this() const { shared_ptr<T const> p( weak_this_ ); BOOST_ASSERT( p.get() == this ); return p; } public: // actually private, but avoids compiler template friendship issues // Note: invoked automatically by shared_ptr; do not call template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const { if( weak_this_.expired() ) { //shared_ptr通過拷貝構造一個臨時的shared_ptr,然后賦值給weak_ptr weak_this_ = shared_ptr<T>( *ppx, py ); } } private: mutable weak_ptr<T> weak_this_; }; } // namespace boost
template<class Y> explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete { boost::detail::sp_enable_shared_from_this( this, p, p ); } template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe ) { if( pe != 0 ) { //調(diào)用 enable_shared_from_this對象的函數(shù) pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) ); } }
可見該類包含一個weak_ptr,并在shared_ptr構造時,將weak_ptr初始化。shared_from_this()返回一個用weak_ptr拷貝構造的shared_ptr(shared_ptr有此構造函數(shù))。
為什么一定是weak_ptr,換成shared_ptr是否可以?
類內(nèi)不能包含指向自身的shared_ptr ,否則它會無法析構。
#include <iostream> #include <memory> class Demo { public: Demo() { std::cout << "constructor" << std::endl; } ~Demo() { std::cout << "destructor" << std::endl; } void StoreDemo(std::shared_ptr<Demo> ptr) { m_ptr = ptr; } private: std::shared_ptr<Demo> m_ptr; }; int main() { std::shared_ptr<Demo> d_ptr(new Demo()); d_ptr->StoreDemo(d_ptr); // this line is the bug return 0; }
執(zhí)行以上操作后d_ptr的引用計數(shù)變成2,因此當main結束時,無法執(zhí)行其析構函數(shù)。
循環(huán)引用
如果兩個類互相包含指向?qū)Ψ降膕hared_ptr,就會造成循環(huán)引用。導致引用計數(shù)失效,內(nèi)存無法釋放。
#include <iostream> #include <memory> class DemoB; class DemoA { public: DemoA() { std::cout << "DemoA()" << std::endl; } ~DemoA() { std::cout << "~DemoA()" << std::endl; } void Set_Ptr(std::shared_ptr<DemoB>& ptr) { m_ptr_b = ptr; } private: std::shared_ptr<DemoB> m_ptr_b; }; class DemoB { public: DemoB() { std::cout << "DemoB()" << std::endl; } ~DemoB() { std::cout << "~DemoB()" << std::endl; } void Set_Ptr(std::shared_ptr<DemoA>& ptr) { m_ptr_a = ptr; } private: std::shared_ptr<DemoA> m_ptr_a; }; int main() { std::shared_ptr<DemoA> ptr_a(new DemoA()); std::shared_ptr<DemoB> ptr_b(new DemoB()); ptr_a->Set_Ptr(ptr_b); ptr_b->Set_Ptr(ptr_a); std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl; return 0; }
這種情況下 A、B的引用計數(shù)都是2,因此無法析構。
解決該問題,需要將其中一個類的shared_ptr換成weak_ptr。
到此這篇關于C++智能指針shared_ptr與weak_ptr的實現(xiàn)分析的文章就介紹到這了,更多相關C++ shared_ptr與weak_ptr內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
linux c程序中獲取shell腳本輸出的實現(xiàn)方法
以下是對在linux下c程序中獲取shell腳本輸出的實現(xiàn)方法進行了詳細的分析介紹,需要的朋友可以過來參考下2013-08-08C++實現(xiàn)LeetCode(13.羅馬數(shù)字轉(zhuǎn)化成整數(shù))
這篇文章主要介紹了C++實現(xiàn)LeetCode(13.羅馬數(shù)字轉(zhuǎn)化成整數(shù)),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07解析VScode在Windows環(huán)境下c_cpp_properties.json文件配置問題(推薦)
這篇文章主要介紹了解析VScode在Windows環(huán)境下c_cpp_properties.json文件配置問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-05-05