C++【智慧指標】
智慧指標的作用:
堆記憶體的申請和釋放都由程式設計師自己管理。
普通指標:需要程式設計師手動釋放,容易造成記憶體洩漏(比如忘記手動釋放,或者程式異常)和二次釋放的問題。
智慧指標:程式結束自己釋放,不需要程式設計師手動釋放。所以不存在記憶體洩漏
智慧指標其實就是利用一種叫做RAII(資源獲取即初始化)的技術對普通指標進行封裝。所以智慧指標的實質是個物件(模板類)
智慧指標的作用是防止忘記呼叫delete釋放記憶體和程式異常沒有delete,或者多次釋放了同一個指標。
智慧指標底層程式碼實現【沒考慮執行緒安全的】
#include<stdio.h> #include<iostream> #include<list> using namespace std; class ResList { public: // 給ptr資源增加引用計數 void addRef(void *ptr) { list<Node>::iterator it1 = nodelist.begin(); while (it1 != nodelist.end()) { if (it1->_resptr == ptr) { it1->_count++; } ++it1; } nodelist.push_back(Node(ptr)); } // 給ptr資源減少引用計數 int delRef(void *ptr) { list<Node>::iterator it1 = nodelist.begin(); while (it1 != nodelist.end()) { if (it1->_resptr == ptr) { if (it1->_count == 1) //只有最後一個指標時,析構 { nodelist.erase(it1); return 0; } else //還有好多指標指向一個資源,指標數減一 { it1->_count--; return it1->_count; } } ++it1; } return -1; } private: struct Node { Node(void *ptr = NULL) :_resptr(ptr), _count(1){} void *_resptr; //資源 int _count; //指標數 }; list<Node> nodelist; }; template<typename T> class CSmartPtr { public: CSmartPtr(T*ptr = NULL) :mptr(ptr) { if (mptr != NULL) reslist.addRef(ptr); } CSmartPtr(const CSmartPtr<T> &src) :mptr(src.mptr) { if (mptr != NULL) reslist.addRef(mptr); } CSmartPtr<T>& operator=(const CSmartPtr<T> &src) { if (this == &src) return *this; if (reslist.delRef(mptr) == 0) { delete mptr; } mptr = src.mptr; reslist.addRef(mptr); return *this; } ~CSmartPtr() { if (reslist.delRef(mptr) == 0) { delete mptr; } } T& operator*(){ return *mptr; } T* operator->(){ return mptr; } private: T*mptr; static ResList reslist; }; template<typename T> ResList CSmartPtr<T>::reslist;
智慧指標的迴圈/交叉引用問題
class A
{
public:
shared_ptr<B> _ptrb;
}
class B
{
public:
shared_ptr<A> _ptra;
}
shared_ptr<A> ptra(new A());
shared_ptr<B> ptrb(new B());
//迴圈引用
ptra->_ptrb = ptrb;
ptrb->_ptra = ptra;
此問題發生的原因是:強智慧指標會改變資源引用計數,在迴圈引用的時候,引用計數被多加了一次。所以該析構的時候,引用計數還為1,則不進行析構操作
解決方法:在建立物件的地方使用強智慧指標,其他地方一律使用弱智慧指標(因為弱智慧指標不能改變資源的引用計數)
弱智慧指標不能訪問物件的方法,要訪問物件,就需要轉換為強智慧指標
弱智慧指標提升為強智慧指標的方法:
shared_ptr<A> ptra = _ptra.lock();
if(ptra != NULL) //判斷是否為NULL,從而判斷是否提升成功。當提升成功後,資源的引用計數隨即加一
{
ptra -> func();
}
庫裡面提供的智慧指標
標頭檔案:#include<memory>
不帶引用計數的智慧指標===》會發生淺拷貝
auto_ptr -- 會將原來的auto_ptr置空,只有最新的auto_ptr才能訪問資源(就是一塊資源只能一個指標訪問,釋放舊指標)
因為會釋放舊的指標,所以我們禁止使用該指標
scope_ptr
unique_ptr --- 與shared_ptr不同,unique_ptr擁有它所指向的物件,在某一時刻,只能有一個unique_ptr指向特定的物件。當unique_ptr被銷燬時,它所指向的物件也會被銷燬。因此不允許多個unique_ptr指向同一個物件,所以不允許拷貝與賦值。
帶引用計數的智慧指標
shared_ptr 強智慧指標【可改變資源的引用計數】,是執行緒安全的
weak_ptr 弱智慧指標【不能改變資源的引用計數】
沒有過載operator*和->,它的最大作用在於協助shared_ptr工作,像旁觀者那樣觀測資源的使用情況
相關資料:
C++11中智慧指標的原理、使用、實現
C++面試題(四)——智慧指標的原理和實現
unique_ptr的使用和陷阱: https://blog.csdn.net/qq_33266987/article/details/78784286