這篇文章建大的介紹瞭如何編寫一個智能指針。
介紹:
什麼是智能指針?答案想必你們都知道,智能指針的目的就是更好的管理好內存和動態分配的資源,智能指針是一個智能的指針,顧名思義,他能夠幫助咱們管理內存。沒必要擔憂內存泄露的問題。實際上,智能指針是一個行爲相似於指針的類,經過這個類咱們來管理動態內存的分配和銷燬。方便客戶端的使用。相比於通常指針,智能指針主要體如今它使用的容易和便捷性。
轉載請註明出處: http://blog.csdn.net/elfprincexu
使用通常指針的問題:
通常狀況下咱們使用指針的問題是什麼?答案是內存管理,簡單來看下面的一個例子:
- char* pName = new char[1024];
- SetName(pName);
- if(null != pName)
- {
- delete[] pName;
- }
在上面一段代碼中,咱們會遇到bug呢? 頗有可能在分配內存的時候就出錯了,有可能在被調用的時候指針誤操做了,也有可能在其餘地方操做了。答案太多太多了
咱們仍是從一個實際的例子開始吧,首先看下面的例子:
- class Person
- {
- int age;
- char* pName;
- public:
- Person(): pName(0),age(0){}
- Person(char* pName, int age): pName(pName), age(age){}
- ~Person(){}
-
- void Display()
- {
- printf("Name = %s Age = %d \n", pName, age);
- }
- void Shout()
- {
- printf("Ooooooooooooooooo",);
- }
- };
如今,咱們開始使用這個類
- void main()
- {
- Person* pPerson = new Person("Scott", 25);
- pPerson->Display();
- delete pPerson;
- }
咱們能夠看到,每次咱們新建一個person空間,都要對內存釋放,不然就有可能形成內存泄露。
那咱們能不能想象一下,存在一個相似指針的類來幫助咱們管理內存。
- template < typename T > class SP
- {
- private:
- T* pData; // Generic pointer to be stored
- public:
- SP(T* pValue) : pData(pValue){}
- ~SP()
- {
- delete pData;
- }
-
- T& operator* ()
- {
- return *pData;
- }
-
- T* operator-> ()
- {
- return pData;
- }
- };
-
- void main()
- {
- SP<PERSON> p(new Person("Scott", 25));
- p->Display();
- // Dont need to delete Person pointer..
- }
經過泛型編程,咱們可使用任何類型的指針,可是上面還不是完美,考慮一下下面的案例
- void main()
- {
- SP<PERSON> p(new Person("Scott", 25));
- p->Display();
- {
- SP<PERSON> q = p;
- q->Display();
- // Destructor of Q will be called here..
- }
- p->Display();
- }
咱們發現,p和q指向同一個實例,因爲SP類沒有定義拷貝函數,系統自動生成一個默認的拷貝函數,實現的是淺賦值,該內存空間被釋放了兩次!
因此,咱們引入Reference Counting的智能指針相當重要,經過對實例被引用的次數來決定該實例是否須要被釋放。
- class RC
- {
- private:
- int count; // Reference count
-
- public:
- void AddRef()
- {
- // Increment the reference count
- count++;
- }
-
- int Release()
- {
- // Decrement the reference count and
- // return the reference count.
- return --count;
- }
- };
如今咱們有了一個RC類,這個類只作被引用的次數,惟一的數據成員就是用來跟蹤被引用的次數。
結合咱們剛纔的SP類,咱們稍做改動
- template < typename T > class SP
- {
- private:
- T* pData; // pointer
- RC* reference; // Reference count
-
- public:
- SP() : pData(0), reference(0)
- {
- // Create a new reference
- reference = new RC();
- // Increment the reference count
- reference->AddRef();
- }
-
- SP(T* pValue) : pData(pValue), reference(0)
- {
- // Create a new reference
- reference = new RC();
- // Increment the reference count
- reference->AddRef();
- }
-
- SP(const SP<T>& sp) : pData(sp.pData), reference(sp.reference)
- {
- // Copy constructor
- // Copy the data and reference pointer
- // and increment the reference count
- reference->AddRef();
- }
-
- ~SP()
- {
- // Destructor
- // Decrement the reference count
- // if reference become zero delete the data
- if(reference->Release() == 0)
- {
- delete pData;
- delete reference;
- }
- }
-
- T& operator* ()
- {
- return *pData;
- }
-
- T* operator-> ()
- {
- return pData;
- }
-
- SP<T>& operator = (const SP<T>& sp)
- {
- // Assignment operator
- if (this != &sp) // Avoid self assignment
- {
- // Decrement the old reference count
- // if reference become zero delete the old data
- if(reference->Release() == 0)
- {
- delete pData;
- delete reference;
- }
-
- // Copy the data and reference pointer
- // and increment the reference count
- pData = sp.pData;
- reference = sp.reference;
- reference->AddRef();
- }
- return *this;
- }
- };
接下來咱們看下客戶端調用狀況
- void main()
- {
- SP<PERSON> p(new Person("Scott", 25));
- p->Display();
- {
- SP<PERSON> q = p;
- q->Display();
- // Destructor of q will be called here..
-
- SP<PERSON> r;
- r = p;
- r->Display();
- // Destructor of r will be called here..
- }
- p->Display();
- // Destructor of p will be called here
- // and person pointer will be deleted
- }
接下來,咱們着重分析下上面的調用狀況:
1. SP<PERSON> p(new Person("Scott",25));
當咱們建立一個新的智能指針的時候,他的參數類型爲Person, 參數爲一個Person的普通指針, 智能指針p中的狀況是
構造函數被調用,pData 複製新建立的person指針, 同時新建一個RC成員,同時RC調用addReference()函數, reference.count =1 ;
2. SP<PERSON> q = p;
接下來,咱們有定義了一個新的SP智能指針q, 調用SP類的拷貝構造函數,q的pData一樣複製p的pData的值,q的reference拷貝p的reference值
同時,咱們發現,q的reference.count加1, 如今 q的reference.count =2;
3. SP<PERSON> r; r = p;
接下來,咱們建立一個新的空的智能指針r,並調用assigne operator 賦值函數初始化,一樣,因爲r != p, 因此原來的r的空間會被釋放, 而後將p的空間複製給r。
這個時候r的pData一樣指向Person實例的地址,p的reference複製p的reference,而且對reference加1. 如今 r的reference.count =3.
4. 因爲 r,q 生命域到達,rq 的析構函數前後被調用。
r首先被析構, 會對reference.count減一,等於2,發現還沒到0, 因此不會釋放 pdata 和 reference
q其次被析構,會對reference.count減一,等於1,發現還沒到0, 因此不會釋放 pdata 和 reference
5. p的生命域到達,p調用析構函數
p最後被析構,會對reference.count減一,等於0,發現到0, 因此釋放 pdata 和 reference。 此時pdata 就是一開始新建立的Person空間,因此person會被釋放,同時Reference也會被釋放。
總結:整個過程當中,咱們只建立了一次Person實例和Reference實例, 但最多有三個智能指針同時指向他們,經過對實例的被引用次數記錄,來「智能」的判斷何時釋放真正的內存空間。