boost 智能指針庫

內存管理需要注意的點

  • 內存泄漏
  • 野指針
  • 訪問越界

爲了避免這些問題,智能指針採用RAII機制 —資源獲取既初始化

  • 所有初始化操作移到對象的構造函數中
  • 所有的釋放操作都放在對象的析構函數裏
  • 適當的異常處理代碼來應付對象構造期間丟出的異常(分配內存的時候)

好處:對象創建後,用戶能開始正確使用對象,不用擔心對象的有效性或者是否還要作進一步的初始化操作。

在這裏插入圖片描述

scoped_ptr 不能拷貝,賦值。

侯捷大師說過,源碼面前,了無祕密

於是就去找一下源碼,源碼中將 拷貝賦值函數設置爲 private,並且函數裏面爲空語句。
不適用於stl 類似的容器中

weak_ptr weak_ptr 可以說具有觀察權。
準確的說不能算作一種智能指針,它的出現是用來彌補 share_ptr 的缺點,例如循環引用。
它有一個lock成員函數,可以將弱引用轉化爲 share_ptr。

其中最常用的就是share_ptr了,它也被收入到了c++ 11 標準中

  • share_ptr 用了設計模式中的 代理模式,用share_ptr 代理指針,所以它獲得了指針相關的操作,比如解引用等等
  • 不可以使用變址操作符(因爲指針越界大部分是因爲變址操作符導致的)。
  • share_ptr 不需要手動的調用類似release 方法。
  • share_ptr 只會所有權轉移 --rest()

share_ptr 源碼摘要

template<class T>
class shared_ptr
{
	//創建一個持有空指針的share_ptr,use_count = 0 && get() == NULL
	shared_ptr() BOOST_NOEXCEPT : px( 0 ), pn() // never throws in 1.30+{}
//獲得一個指向類型T的指針p的管理權,Y 必須能夠轉換爲T類型	
template<class Y>  
explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete
//作用同上,增加了一個構造函數D,是一個仿函數對象,代表刪除器
template<class Y, class D> 
shared_ptr( Y * p, D d ): px( p ), pn( p, d )

//析構函數:資源引用計數減1 同時判斷引用計數爲0,並且 管理的資源不爲NULL,並且判斷是否有刪除器,如果有刪除器,調用刪除器,如果沒有刪除器,調用delete
~shared_ptr();
};

這裏爲什麼要有刪除器呢?
例如:fopen(),fclose() 操作打開的資源。這是delete 沒用的。

構造函數獲得管理權,
拷貝賦值操作 共享管理權

至於shared_ptr 的成員函數,這裏就只說一下reset();
void reset();
template<class Y> void reset(Y* p);
template<class Y,class D> void reset(Y* p);

reset(): 如果該share_ptr 擁有某個管理權的話,它會將引用計數減1,如果資源計數器爲0,並且指針不爲NULL,刪除原來共享的資源。將指針賦爲 NULL

template void reset(Y* p); --轉移管理權
首先判斷該share_ptr 是否擁有管理權,如果有的話,,它會將引用計數減1,如果資源計數器爲0,它會釋放掉原來所指向的資源或指針,讓他delete,然後它會將它內部的T 類型的指針,設置爲參數 Y 的指針,這樣就獲得了 Y 類型 的 p 指針的管理權,然後將引用計數設爲1。

shared_ptr 使用時出現的常見問題

1.shared_ptr 多次引用同一內存數據導致程序崩潰

No code , No BB


class Foo
{
public:

	Foo(string s) : m_s(s) {}
	~Foo()
	{
		cout << "Foo 開始析構" << endl;
	}
private:

		string m_s;
};
int main()
{
	Foo* pFoo = new Foo("test");

	shared_ptr<Foo> p1(pFoo);
	shared_ptr<Foo> p2(pFoo);


	return 0;
}

運行結果:
在這裏插入圖片描述
可以看到,將Foo() 析構了兩次。

解決方案:
1.使用匿名 new操作,資源獲取既初始化

shared_ptr<Foo> p1(new Foo("test"));

2.使用boost 工廠函數

2.shared_ptr 循環引用導致內存泄露

class B; // 前置聲明

class A {
public:
	A()
	{
		cout << "A 構造" << endl;

	}
	~A()
	{
		cout << "A 析構" << endl;
	}
    	shared_ptr<B> ptr;

};

class B {
public:
	B()
	{
		cout << "B 構造" << endl;
	}
	~B()
	{
		cout << "B 析構 " << endl;
	}
    	shared_ptr<A> ptr;

};

int main()
{
        shared_ptr<A> pa(new A());
        shared_ptr<B> pb(new B());
        pa -> ptr = pb;     // 1
        pb -> ptr = pa;     //2 

	
	return 0;
}

運行結果:
在這裏插入圖片描述
結果沒有調用析構函數。
什麼原因呢?

當執行 1 時,pb 指針的引用計數加 1
當執行 2 時,pa 指針的引用計數加 1

離開主函數時,兩個指針的引用計數減一,不爲 0 ,所以不調用析構函數。

解決方法:
weak_ptr

1.從上面的例子可以看出,引用計數是一種很便利的內存管理,但是有一個缺點,那就是不能管理循環引用或自引用對象,然後就引入了 weak_ptr 。

2.它是與shared_ptr 同時使用的,它更像是shared_ptr 的助手而不是智能指針,因爲它不具備普通指針的行爲,沒有重載operaotr * 和 -> 操作符。這是特意的。這樣他就不能共享指針,不能操作資源,這正是它"弱"的原因,它最大的作用是協助shared_ptr 工作,像旁觀者那樣觀察資源的使用情況

3.weak_ptr 可以從一個shared_ptr 或另外一個weal_ptr 構造,從而獲得資源的觀察權,但weak_ptr 並沒有共享資源,它的構造並不會引起引用計數的增加,同事它的析構也不會引起引用計數的減少,它僅僅是觀察者。

4.weak_ptr 的lock 成員函數 的返回值是一個shared_ptr 類型的指針。

make_shared< T >模板工廠函數

1.用於不需要使用刪除器的情況下(僅僅使用 new /delete 進行內存分配和析構的類上面)
有數shared_ptr 顯式的消除了delete 操作符的調用,因此使用該函數也可以顯式的消除了 new 操作符的調用

2.調用該函數比直接創建shared_ptr 對象的方式快且高效,因爲它內部僅分配一次內存,消除了shared_ptr 構造時的開銷,建議在滿足情況的基礎上儘量使用該函數

3.它具有可變模板參數特性,如果c++ 編譯器支持c++ 11 的可變參數模板特性,那麼該工廠函數的參數數量沒有限制,否則它只能接受最多 10 個參數被傳遞到 T 的構造函數參數中去。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章