RAII&智能指針

RAII:資源分配及初始化。但是這個翻譯并沒有顯示出這個慣用法的真正內(nèi)涵。RAII的好處在于它提供了一種資源自動管理的方式,當出現(xiàn)異常,回滾等現(xiàn)象時,RAII可以正確的釋放資源。

合山網(wǎng)站建設(shè)公司成都創(chuàng)新互聯(lián)公司,合山網(wǎng)站設(shè)計制作,有大型網(wǎng)站制作公司豐富經(jīng)驗。已為合山數(shù)千家提供企業(yè)網(wǎng)站建設(shè)服務(wù)。企業(yè)網(wǎng)站搭建\外貿(mào)網(wǎng)站制作要多少錢,請找那個售后服務(wù)好的合山做網(wǎng)站的公司定做!

內(nèi)存泄漏會導(dǎo)致:

            1.內(nèi)存耗盡 2.其他程序可能用不了了 3.程序崩潰

在資源的獲取和釋放之間,我們通常會使用資源,但常常一些不可預(yù)計的異常會在資源使用過程中產(chǎn)生,這就使資源沒有得到正確的釋放。但是我們其實是可以捕捉異常的,但是捕捉異常會使得代碼冗余雜亂,量大而且可讀性比較低。

比如:

void DoSomething()
{
    int *p=new int(1);
    cout<<"DoSomething()"<<endl;
}

void Test()
{
   try
   {
      DoSomething();
   }
   catch(...)
   {
      delete p;
      throw;
   }
}

這樣短短一個代碼看起來卻很雜亂,而且也不好控制。

所以我們引出了智能指針。智能指針的實現(xiàn)原理就是RAII。

智能指針(smart pointer)是存儲指向動態(tài)分配(堆)對象指針的類,用于生存期控制,能夠確保自動正確的銷毀動態(tài)分配的對象,防止內(nèi)存泄露。它的一種通用實現(xiàn)技術(shù)是使用引用計數(shù)(reference count)。智能指針類將一個計數(shù)器與類指向的對象相關(guān)聯(lián),引用計數(shù)跟蹤該類有多少個對象共享同一指針。每次創(chuàng)建類的新對象時,初始化指針并將引用計數(shù)置為1;當對象作為另一對象的副本而創(chuàng)建時,拷貝構(gòu)造函數(shù)拷貝指針并增加與之相應(yīng)的引用計數(shù);對一個對象進行賦值時,賦值操作符減少左操作數(shù)所指對象的引用計數(shù)(如果引用計數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計數(shù);調(diào)用析構(gòu)函數(shù)時,構(gòu)造函數(shù)減少引用計數(shù)(如果引用計數(shù)減至0,則刪除基礎(chǔ)對象)。

下面介紹幾個Boost庫中的智能指針。

  1. AutoPtr

AutoPtr的實現(xiàn)主要是管理權(quán)轉(zhuǎn)移。它沒有考慮引用計數(shù),當用一個對象構(gòu)造另一個對象時,會轉(zhuǎn)移這種擁有關(guān)系。

template<class T>
class AutoPtr
{
	//friend ostream& operator<< <T>(ostream& os, const AutoPtr<T>& ap);
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
	{}
	~AutoPtr()
	{
		if (_ptr!=NULL)
		{
			delete _ptr;
		}
	}
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)
	{
		ap._ptr = NULL;
	}
public:
	AutoPtr<T>& operator=(const AutoPtr<T>& ap)
	{
		if (this != ap)
		{
			delete _ptr;
			_ptr = ap._ptr;
			ap._ptr = NULL;
			return _ptr;
		}
	}

	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
public:
	T* GetPtr()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

舊版的AutoPtr在它的成員函數(shù)中有一個變量owner,在構(gòu)架對象的時候owner為true。當用它去構(gòu)建新的對象時,他自己的owner變?yōu)閒alse,新對象的owner為true。賦值重載的時候也是,新對象的owner是true。這樣在析構(gòu)的時候只要判斷owner的狀態(tài)是否為true,當為true時,將這塊空間delete即可。

template<class T>
class AutoPtr
{
public:
	AutoPtr(T* ptr)
		:_ptr(ptr)
		, owner(true)
	{}
	~AutoPtr()
	{
		if (owner)
		{
			delete _ptr;
		}
	}
	AutoPtr(AutoPtr<T>& ap)
		:_ptr(ap._ptr)
		, owner(true)
	{
		ap._ptr = NULL;
		ap.owner = false;
	}
public:
	AutoPtr<T>& operator=(const AutoPtr<T>& ap)
	{
		if (this != ap)
		{
			delete _ptr;
			_ptr = ap._ptr;
			ap.owner = false;
			owner = true;
			return _ptr;
		}
	}
}

看起來舊的比新的好。AutoPtr最大的缺點就是只能有一個指針管理一塊空間。那么為什么新的還取代了舊的呢?看下面

AutoPtr<int> ap1(new int(1));
 
 if (1)
 
 {
 
  AutoPtr<int> ap2(ap1);
 
 }
 
 *ap1 = 3;

這段代碼是用舊版本實現(xiàn)的智能指針(ap1)指向一個動態(tài)開辟的內(nèi)存,然后在if條件語句中又有一個ap2指向這塊內(nèi)存,我們會知道,根據(jù)舊版的智能指針的實現(xiàn)原理,ap1的_owner為false,ap2的_owner為true。那么除了if條件語句的局部作用域,ap2就自動調(diào)用析構(gòu)函數(shù)釋放內(nèi)存,那么當我們在外面*ap1=3時,訪問到的是一塊已經(jīng)被釋放了的內(nèi)存,那么程序這時就會出現(xiàn)問題。

如果是新版的auto_ptr,它提供了一個公有成員函數(shù)GetPtr(),可以獲取指針_ptr,當發(fā)生這種情況時,它可以先判斷_ptr是否為空,然后才去訪問內(nèi)存。舊版本這樣做是無用的,因為ap1的_ptr并不為空。

2.ScopePtr//守衛(wèi)指針

這個類型的指針簡單來說就是簡單粗暴的防拷貝。

template<class T>
class ScopedPtr
{
public:
	ScopedPtr(T* ptr)
		:_ptr(ptr)
	{}
	~ScopedPtr()
	{
		if (_ptr != NULL)
		{
			delete _ptr;
		}
	}
protected:
	ScopedPtr(const ScopedPtr<T>& sp);
	ScopedPtr<T>& operator=(ScopedPtr<T>& sp);
public:
	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};

將拷貝構(gòu)造函數(shù)和賦值重載函數(shù)只聲明不實現(xiàn),而且是Protected.就是為了防止在類外實現(xiàn)的情況發(fā)生,簡單粗暴哈哈。RAII&智能指針

3.SharedPtr

采用引用計數(shù),構(gòu)造一個對象時計數(shù)器為1,用這個對象去拷貝構(gòu)造另一個新的對象時,計數(shù)器增加1.去賦值給另一個對象時,計數(shù)器同樣加1.析構(gòu)時計數(shù)器減1.當計數(shù)器值為1時,便可delete這塊空間。

template<class T>
class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{}
	~SharedPtr()
	{
		_Release();
	}
	SharedPtr(SharedPtr<T>& sp)
	{
		_ptr = sp._ptr;
		_pcount = sp._pcount;
		(*_pcount)++;
	}
public:
	SharedPtr<T>& operator=(const SharedPtr<T>& sp)
	{
		if (_ptr != sp._ptr)
		{
			_Release();
			_ptr = sp._ptr;
			_pcount = sp._pcount;
			(*_pcount)++;
		}
		return *this;
	}

	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	long UseCount()
	{

		return *(_pcount);
	}
	T* GetPtr()
	{
		return _ptr;
	}

private:
	T* _ptr;
	int* _pcount;
	Del _del;

	void _Release()
	{
		if (--(*_pcount) == 0)
		{
			_del(_ptr);
			delete(_pcount);
		}
	}
};

在這些指針中最常用到的就是SharedPtr了。但是SharedPtr也存在問題。

  1. 線程安全(這個問題我這個級別還不能很好地解決哈哈。等著我變成大神,就辦了它。。。)

  2. 循環(huán)引用

    什么是循環(huán)引用呢??給小伙伴舉個例子來說明一下。

void test2()
{
	SharedPtr<Node> cur(new Node(1));
	SharedPtr<Node> next(new Node(2));
	cur->_next = next;
	next->_prev = cur;
}

struct Node
{
	Node(int d)
	:_data(d)
	, _next(NULL)
	, _prev(NULL)
	{}

	int _data;
	SharedPtr<Node> _next;
	SharedPtr<Node> _prev;
};

int main()
{
	test2();
	getchar();
	return 0;
}

運行這段代碼回發(fā)現(xiàn)并沒有輸出。沒有輸出的原因是  cur,next的引用計數(shù)都是2,當出了test2的作用域時,分別調(diào)用析構(gòu)函數(shù),二者的引用計數(shù)都減為1.但是cur靠next->_prev指著,next靠cur->_next指著,兩個都在等對方先delete自己的引用計數(shù)才能減到0,自己才能delete,這就導(dǎo)致二者都不會delete了。要解決這個問題呢,我們就要引用第四種只能指針了。。那就是WeakPtr了。WeakPtr可以說就是為了SharedPtr準備的。因為WeakPtr的構(gòu)造函數(shù)只接受SharedPtr類型的對象。

struct Node
{
	Node(int d)
	:_data(d)
	, _next(NULL)
	, _prev(NULL)
	{}

	int _data;
	WeakPtr<Node> _next;
	WeakPtr<Node> _prev;
};

WeakPtr不增加引用計數(shù)。這樣next->_prev和cur->_next兩個指針就不會增加引用計數(shù),也就不會出現(xiàn)循環(huán)引用的問題了。

3.定制刪除器

當SharedPtr類型的指針遇到其他有些類型的指針時,它就不行了。。。。哦哦。。為什么說他不行了呢,因為SharedPtr只能解決new 出來的東西。那么對于文件類型的指針呢,malloc出來的東西呢。這就需要我們?yōu)樗麄兞可矶ㄖ平鉀Q方法啦。

在這里,又要講到另外一個知識點了。那就是仿函數(shù)。它就是能把對象當做函數(shù)調(diào)用一樣的使用。

template<class T>
struct Less
{
   bool operator()(const T& l,const T& r)
   {
      retrun l>r;
   }
}

int main()
{
   Less less;
   cout<<less(1.2)<<endl;//使用對象去調(diào)用類里面的函數(shù)
}

接下來就是定制刪除器

template<class T ,class Del = Delete<T>>
class SharedPtr
{
public:
	SharedPtr(T* ptr)
		:_ptr(ptr)
		, _pcount(new int(1))
	{}
	SharedPtr(T* ptr,Del del)
		:_ptr(ptr)
		, _pcount(new int(1))
		, _del(del)
	{}
	~SharedPtr()
	{
		_Release();
	}
	SharedPtr(SharedPtr<T,Del>& sp)
	{
		_ptr = sp._ptr;
		_pcount = sp._pcount;
		(*_pcount)++;
	}
public:
	SharedPtr<T, Del>& operator=(const SharedPtr<T, Del>& sp)
	{
		if (_ptr != sp._ptr)
		{
			_Release();
			_ptr = sp._ptr;
			_pcount = sp._pcount;
			(*_pcount)++;
		}
		return *this;
	}

	T& operator*()
	{
		return *_ptr;
	}
	T* operator->()
	{
		return _ptr;
	}
	long UseCount()
	{

		return *(_pcount);
	}
	T* GetPtr()
	{
		return _ptr;
	}

private:
	T* _ptr;
	int* _pcount;
	Del _del;

	void _Release()
	{
		if (--(*_pcount) == 0)
		{
			_del(_ptr);
			delete(_pcount);
		}
	}
};

template<class T>
struct Free                          //free
{
	void operator()(void* ptr)
	{
		cout << "free" << endl;
		free(ptr);
		ptr = NULL;
	}
};

template<class T>
struct Delete                         //delete
{
	void operator()(T* ptr)
	{
		cout << "Del" << endl;
		delete(ptr);
		ptr = NULL;
	}
};

template<class T>
struct Fclose                            //fclose
{
	void operator()(void* ptr)
	{
		cout << "Fclose" << endl;
		fclose((FILE*)ptr);
	}
};

最后我們來總結(jié)一下:

  1. AutoPtr   管理權(quán)轉(zhuǎn)移-》不要使用

  2. ScopePtr  防拷貝-》簡單粗暴

  3. SharedPtr 引用計數(shù)-》增減引用計數(shù),最有一個對象釋放

  4. xxxArray  管理對象數(shù)組-》operator[]

  5. WearPtr   弱指針,輔助SharedPtr.解決循環(huán)引用

說了這么多,比較啰嗦哈RAII&智能指針

網(wǎng)頁題目:RAII&智能指針
標題鏈接:http://muchs.cn/article36/jpjcsg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供用戶體驗、定制網(wǎng)站、面包屑導(dǎo)航、自適應(yīng)網(wǎng)站、關(guān)鍵詞優(yōu)化、網(wǎng)頁設(shè)計公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)