1.程式員明確的進行記憶體釋放
對於c++程式員,最頭腦的莫過於對動態分配的記憶體進行管理了。c++在堆上分配的記憶體,需要程式員負責對分配的記憶體進行釋放。但有時記憶體的釋放看起來並不件很輕鬆的事,如下程式
void func(){ int *p = new int(0); if(一些判斷) { return; } p = new int(1); delete p;}
這個函數沒有任何意義,只為說明問題。func函數至少有三處問題。1.一旦if的判斷條件成立,就會立馬執行返回語句。此時p所指向的記憶體將無法釋放(這個新手一般都會犯的錯誤)。2. p = new int(1);語句,沒有將原來分配在堆上的記憶體釋放,造成原來那塊記憶體永遠不可能釋放直到程式結束。3.這個問題並不是那麼明顯,假設func()函數的代碼很長。
void func(){ int *p = new int(0); ... delete p;}
在int *p = new int(0)和delete p之間有大量代碼,而這中間的代碼可能有代碼已經執行過delete p,並且沒有把p置0。此時p為懸掛指標。後面再執行delete p就會造成執行階段錯誤。 2.一個類似於boost shared_ptr的智能指標管理類
下面是參照c++ primer及effective c++自己寫的一個智能指標管理類。使用SmartPtr,就不再需要關心記憶體的釋放問題了。
#ifndef SMARTPTR_INCLUDE_H#define SMARTPTR_INCLUDE_H#include <iostream>using namespace std;namespace commutil{ template<typename T> class SmartPtr { public: SmartPtr(T *p = 0); SmartPtr(const SmartPtr &); SmartPtr &operator=(const SmartPtr &); ~SmartPtr(); T &operator *(); T *operator ->(); private: void decreaseRef(); T *m_p; int *m_useCnt; }; template<typename T> SmartPtr<T>::SmartPtr(T *p=0):m_p(p) { //調用此建構函式時,預設引用數為1 m_useCnt = new int(1); } template<typename T> SmartPtr<T>::SmartPtr(const SmartPtr &rhs) { //使用複製建構函式,建立新的對象,引用數加1 this->m_p = rhs.m_p; this->m_useCnt = rhs.m_useCnt; (*m_useCnt)++; } template<typename T> SmartPtr<T>::~SmartPtr() { decreaseRef(); } template<typename T> void SmartPtr<T>::decreaseRef() { if(--(*m_useCnt)==0) {//當引用數為0時,釋放heap記憶體 delete m_useCnt; delete m_p; } } template<typename T> T &SmartPtr<T>::operator *() { //重載解引用符 return *m_p; } template<typename T> T *SmartPtr<T>::operator ->() { //重載 ->運算子,返回真實的對象指標 return m_p; } template<typename T> SmartPtr<T> &SmartPtr<T>::operator=(const SmartPtr &anotherPtr) { if(this==&anotherPtr) {//防止自賦值 return *this; } //使用賦值運算子,原來所指對象的引用數減1。 decreaseRef(); m_p = anotherPtr.m_p; m_useCnt = anotherPtr.m_useCnt; ++*m_useCnt;//指向新的對象,所指對象引用數加1 return *this; }}#endif
3.使用SmartPtr的案例分析
3.1第一個SmartPtr的執行個體
void func(){ SmartPtr<int> autoPtr(new int(1));}
在func中,定義了SmartPtr的一個對象autoPtr,並用new int(1)在堆上分配一塊記憶體,將分配的記憶體首地址傳給SmartPtr的建構函式。此時autoPtr的m_useCnt的值為1。當func執行完成時,autoPtr對象超出其範圍,調用autoPtr的解構函式。在解構函式中調用私人的decreaseRef()函數,在decreaseRef()中將autoPtr的m_useCnt所記憶體值減1;此時m_useCnt所指記憶體值為0,執行delete m_useCnt;delete m_p;。至此在建立autoPtr對象時動態分配的記憶體被釋放。
3.2帶有return語句的SmartPtr
void func(){ SmartPtr<int> autoPtr(new int(1)); if(判斷條件) { return; }}
func()函數中的if判斷條件成立時,執行return;語句後,autoPtr超出其作用範圍,記憶體釋放。分析見3.1
3.3 SmartPtr對象管理新的SmartPtr對象
SmartPtr<int> autoPtr1(new int(0)); SmartPtr<int> autoPtr2(new int(1)); autoPtr1 = autoPtr2;
執行autoPtr1 = autoPtr2,由於SmartPtr類重載了賦值運算子。實際執行的是SmartPtr的operator=()函數,在上面這個指派陳述式中,先autoPtr1的m_useCnt所指記憶體值減1。此時m_useCnt所指記憶體值變為0,執行m_p所指記憶體釋放。使用SmartPtr解決了1中所提的第二個問題。並且所有記憶體管理與釋放工作都由SmartPtr進行,程式員不需要再關心什麼時候進行記憶體釋放。
3.4SmartPtr共用對象資源
與auto_ptr不同,SmartPtr使用引用計數機制保證多個SmartPtr對象可以管理同一個對象資源
void func(){ SmartPtr<int> autoPtr1(new int(0)); SmartPtr<int> autoPtr2(autoPtr1);}執行SmartPtr autoPtr2(autoPtr1)調用SmartPtr的拷貝建構函式,在拷貝建構函式中將autoPtr2的m_useCnt及m_p分別指向autoPtr1所指向的記憶體,再將m_useCnt的引用數加1。func()函數結束時,兩次調用SmartPtr的解構函式,兩次解構函式後引用數為0,刪除m_useCnt,m_p所指記憶體。
3.5SmartPtr訪問類成員函數
class A{public: void print() { cout<<"A"<<endl; }};void func(){ A *obj = new A(); obj->print(); delete obj;}
SmartPtr重載了->運算子,可以將SmartPtr對象像指標一樣訪問成員函數。
void func(){ SmartPtr<A> autoPtr(new A()); autoPtr->print();}
4.小結
SmartPtr類實現了記憶體對象”自管理”,使用SmartPtr程式員不再需要關心動態記憶體的釋放問題。與shared_ptr不同,SmartPtr類只能管理指標類型的資源,並且SmartPtr不支援自訂的資源釋放函數。