中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

RAII&智能指針

發布時間:2020-08-08 00:18:47 來源:網絡 閱讀:1099 作者:nna_0914 欄目:編程語言

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

內存泄漏會導致:

            1.內存耗盡 2.其他程序可能用不了了 3.程序崩潰

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

比如:

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

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

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

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

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

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

  1. AutoPtr

AutoPtr的實現主要是管理權轉移。它沒有考慮引用計數,當用一個對象構造另一個對象時,會轉移這種擁有關系。

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在它的成員函數中有一個變量owner,在構架對象的時候owner為true。當用它去構建新的對象時,他自己的owner變為false,新對象的owner為true。賦值重載的時候也是,新對象的owner是true。這樣在析構的時候只要判斷owner的狀態是否為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;

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

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


2.ScopePtr//守衛指針

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

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;
};

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


3.SharedPtr

采用引用計數,構造一個對象時計數器為1,用這個對象去拷貝構造另一個新的對象時,計數器增加1.去賦值給另一個對象時,計數器同樣加1.析構時計數器減1.當計數器值為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. 循環引用

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

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;
}

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

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

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

WeakPtr不增加引用計數。這樣next->_prev和cur->_next兩個指針就不會增加引用計數,也就不會出現循環引用的問題了。

3.定制刪除器

當SharedPtr類型的指針遇到其他有些類型的指針時,它就不行了。。。。哦哦。。為什么說他不行了呢,因為SharedPtr只能解決new 出來的東西。那么對于文件類型的指針呢,malloc出來的東西呢。這就需要我們為他們量身定制解決方法啦。

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

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;//使用對象去調用類里面的函數
}

接下來就是定制刪除器

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);
	}
};

最后我們來總結一下:

  1. AutoPtr   管理權轉移-》不要使用

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

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

  4. xxxArray  管理對象數組-》operator[]

  5. WearPtr   弱指針,輔助SharedPtr.解決循環引用


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

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

阿拉善左旗| 观塘区| 灌南县| 丰宁| 博白县| 台东市| 九寨沟县| 简阳市| 开封县| 台北市| 高唐县| 永州市| 海口市| 汤原县| 昆明市| 当阳市| 芷江| 玉龙| 衡山县| 万盛区| 桐梓县| 策勒县| 南投市| 江门市| 嘉鱼县| 苍溪县| 孟津县| 青岛市| 肥西县| 邳州市| 神池县| 武山县| 双峰县| 福州市| 云林县| 思茅市| 郯城县| 大兴区| 嘉鱼县| 鄂尔多斯市| 林芝县|