今天寫程式時想用智能指標與vector容器一起解決指標數組的成員管理問題。在我的程式中我用到了多個指標容器,這樣就導致了一個問題,這些指標容器的清空非常繁瑣,我不能僅僅調用一次clear就能完成,我需要每次從容器中取出一個元素,然後釋放掉這個元素所佔的記憶體,之後再清除掉指標,然後再清空容器。我覺得很是麻煩,於是想將其改進,我不需要去管理容器內的這些指標,當容器被清空或者銷毀時,我希望這些元素所佔的記憶體也都被釋放掉。於是,我想到了智能指標,現將今天的整個過程記錄下來,以免日後再犯同樣的錯誤,同時也希望為存在同樣疑惑的同學通俗的說明這個問題。
所謂智能指標,我所理解的就是其能夠判斷該指標的生存周期,在指標的生存周期外對其所佔用的記憶體進行自動釋放,而不需要程式員進行主動控制。對於c++的智能指標,我首先想到的是《Thingking in C++》中提到的auto_ptr,於是我就首先想採用auto_ptr來實現。首先定義一個類,重載new和delete操作符,以便我們能看到具體在使用過程中發生了什麼。
class CSmart{public:CSmart(void);~CSmart(void);CSmart(int i):i(i){}private:int i;public:static void * operator new(size_t size){void *p = ::operator new(size);cout<<"Allocting TraceHeap object on the heap at address "<<p<<endl;return p;}static void operator delete(void *p){cout<<"Deleting TraceHeap object at address "<<p<<endl;::operator delete (p);}int getVal() const { return i ;}};
然後我們首先來實驗下auto_ptr的作用
void test_smart_ptr(){auto_ptr<CSmart> pCSmart(new CSmart(5));cout<<pCSmart->getVal()<<endl;}int main(){ test_smart_ptr();system("pause");return 0;}
輸入結果如下:
我們看到在程式運行結束的時候申請的那塊記憶體被成功釋放,程式員並沒有主動釋放。好的,這正是我們想要的功能,那麼我們試試賦值的結果,我們把test_smart_ptr的代碼改為如下內容:
void test_smart_ptr(){auto_ptr<CSmart> pCSmart(new CSmart(5));cout<<"ori "<<pCSmart->getVal()<<endl;auto_ptr<CSmart> pCopy;pCopy = pCSmart;cout<<"copy "<<pCopy->getVal()<<endl;//cout<<pCSmart->getVal()<<endl;//Error! 記憶體讀取錯誤}
我們發現ok,這一切都是沒有問題的,那麼我們把上面代碼中注釋掉的那一行取消掉注釋會是什麼樣的結果呢?
我們發現出現了錯誤,程式不能運行了,這可如何是好,從錯誤中我們大概知道是讀取了非法的記憶體位址,那麼為什麼會出現這樣的情況呢,原來是C++標準中定義的auto_ptr中對“=”操作符重載的定義中時將賦值後的值搶奪掉原有值的管理所有權,這樣就導致了原有值的不可用。這樣,我們自然就想到了本來我們要解決的指標容器自動管理的問題,我們知道在向容器中添加元素的時候,其實就是賦值的操作,現在由於“=”問題,導致其不可用。看來,auto_ptr這個東西在一個函數內作為臨時變數是比較好用的,其餘的就不那麼好用了,在好多資料上關於auto_ptr的使用有這麼幾點說明:
1、auto_ptr不能共用所有權。
2、auto_ptr不能指向數組
3、auto_ptr不能作為容器的成員。
4、不能通過賦值操作來初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OK
std::auto_ptr<int> p = new int(42); //ERROR
這是因為auto_ptr 的建構函式被定義為了explicit
5、不要把auto_ptr放入容器
那麼,還有沒有其他方法解決本文最開始的問題呢,肯定是有的,於是我們發現了shared_ptr,shared_ptr原來是Boost庫中的內容,現在c++11新標準的發布,將其加入到了c++標準庫中,可以不必使用Boost庫而直接使用。那麼我們來改寫上面的例子:
void test_smart_ptr(){shared_ptr<CSmart> pCSmart(new CSmart(5));cout<<"ori "<<pCSmart->getVal()<<endl;shared_ptr<CSmart> pCopy;pCopy = pCSmart;cout<<"copy "<<pCopy->getVal()<<endl;cout<<"ori again "<<pCSmart->getVal()<<endl;}
結果如下:
我們發現一切正常,好的,那麼我們來用容器和只能指標實現對指標容器的管理吧。
void test_smart_ptr(){vector<shared_ptr<CSmart>> smart_ptr_vector;shared_ptr<CSmart> pCSmart1(new CSmart(5));shared_ptr<CSmart> pCSmart2(new CSmart(10));shared_ptr<CSmart> pCSmart3(new CSmart(15));smart_ptr_vector.push_back(pCSmart1);smart_ptr_vector.push_back(pCSmart2);smart_ptr_vector.push_back(pCSmart3);for(vector<shared_ptr<CSmart>>::iterator it = smart_ptr_vector.begin();it != smart_ptr_vector.end(); it++)cout<<(*it)->getVal()<<endl;//訪問其中的每一個元素}
ok,看運行結果,一切正常,這就是我們要的結果,之後可以按照vector中存入的為普通變數進行處理。