1. 程式人生 > >C++【智慧指標】

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