C++智能指針及其簡單實現

      網友投稿 770 2022-05-30

      原文:http://www.cnblogs.com/xiehongfeng100/p/4645555.html

      C++智能指針及其簡單實現

      本文將簡要介紹智能指針shared_ptr和unique_ptr,并簡單實現基于引用計數的智能指針。

      使用智能指針的緣由

      1. 考慮下邊的簡單代碼:

      1 int main() 2 { 3 int *ptr = new int(0); 4 return 0; 5 }

      就如上邊程序,我們有可能一不小心就忘了釋放掉已不再使用的內存,從而導致資源泄漏(resoure leak,在這里也就是內存泄漏)。

      2. 考慮另一簡單代碼:

      1 int main() 2 { 3 int *ptr = new int(0); 4 delete ptr; 5 return 0; 6 }

      我們可能會心想,這下程序應該沒問題了?可實際上程序還是有問題。上邊程序雖然最后釋放了申請的內存,但ptr會變成空懸指針(dangling pointer,也就是野指針)。空懸指針不同于空指針(nullptr),它會指向“垃圾”內存,給程序帶去諸多隱患(如我們無法用if語句來判斷野指針)。

      上述程序在我們釋放完內存后要將ptr置為空,即:

      1 ptr = nullptr;

      除了上邊考慮到的兩個問題,上邊程序還存在另一問題:如果內存申請不成功,new會拋出異常,而我們卻什么都沒有做!所以對這程序我們還得繼續改進(也可用try...catch...):

      1 #include 2 using namespace std; 3 4 int main() 5 { 6 int *ptr = new(nothrow) int(0); 7 if(!ptr) 8 { 9 cout << "new fails." 10 return 0; 11 } 12 delete ptr; 13 ptr = nullptr; 14 return 0; 15 }

      3. 考慮最后一簡單代碼:

      1 #include 2 using namespace std; 3 4 int main() 5 { 6 int *ptr = new(nothrow) int(0); 7 if(!ptr) 8 { 9 cout << "new fails." 10 return 0; 11 } 12 // 假定hasException函數原型是 bool hasException() 13 if (hasException()) 14 throw exception(); 15 16 delete ptr; 17 ptr = nullptr; 18 return 0; 19 }

      當我們的程序運行到“if(hasException())”處且“hasException()”為真,那程序將會拋出一個異常,最終導致程序終止,而已申請的內存并沒有釋放掉。

      當然,我們可以在“hasException()”為真時釋放內存:

      1 // 假定hasException函數原型是 bool hasException() 2 if (hasException()) 3 { 4 delete ptr; 5 ptr = nullptr; 6 throw exception(); 7 }

      但,我們并不總會想到這么做。而且,這樣子做也顯得麻煩,不夠人性化。

      如果,我們使用智能指針,上邊的問題我們都不用再考慮,因為它都已經幫我們考慮到了。

      因此,我們使用智能指針的原因至少有以下三點:

      1)智能指針能夠幫助我們處理資源泄露問題;

      2)它也能夠幫我們處理空懸指針的問題;

      3)它還能夠幫我們處理比較隱晦的由異常造成的資源泄露。

      智能指針

      自C++11起,C++標準提供兩大類型的智能指針:

      1. Class shared_ptr實現共享式擁有(shared ownership)概念。多個智能指針可以指向相同對象,該對象和其相關資源會在“最后一個引用(reference)被銷毀”時候釋放。為了在結構復雜的情境中執行上述工作,標準庫提供了weak_ptr、bad_weak_ptr和enable_shared_from_this等輔助類。

      2. Class unique_ptr實現獨占式擁有(exclusive ownership)或嚴格擁有(strict ownership)概念,保證同一時間內只有一個智能指針可以指向該對象。它對于避免資源泄露(resourece leak)——例如“以new創建對象后因為發生異常而忘記調用delete”——特別有用。

      注:C++98中的Class auto_ptr在C++11中已不再建議使用。

      shared_ptr

      幾乎每一個有分量的程序都需要“在相同時間的多處地點處理或使用對象”的能力。為此,我們必須在程序的多個地點指向(refer to)同一對象。雖然C++語言提供引用(reference)和指針(pointer),還是不夠,因為我們往往必須確保當“指向對象”的最末一個引用被刪除時該對象本身也被刪除,畢竟對象被刪除時析構函數可以要求某些操作,例如釋放內存或歸還資源等等。

      所以我們需要“當對象再也不被使用時就被清理”的語義。Class shared_ptr提供了這樣的共享式擁有語義。也就是說,多個shared_ptr可以共享(或說擁有)同一對象。對象的最末一個擁有者有責任銷毀對象,并清理與該對象相關的所有資源。

      shared_ptr的目標就是,在其所指向的對象不再被使用之后(而非之前),自動釋放與對象相關的資源。

      下邊程序摘自《C++標準庫(第二版)》5.2.1節:

      1 #include 2 #include 3 #include 4 #include 5 using namespace std; 6 7 int main(void) 8 { 9 // two shared pointers representing two persons by their name 10 shared_ptr pNico(new string("nico")); 11 shared_ptr pJutta(new string("jutta"), 12 // deleter (a lambda function) 13 [](string *p) 14 { 15 cout << "delete " << *p << endl; 16 delete p; 17 } 18 ); 19 20 // capitalize person names 21 (*pNico)[0] = 'N'; 22 pJutta->replace(0, 1, "J"); 23 24 // put them multiple times in a container 25 vector> whoMadeCoffee; 26 whoMadeCoffee.push_back(pJutta); 27 whoMadeCoffee.push_back(pJutta); 28 whoMadeCoffee.push_back(pNico); 29 whoMadeCoffee.push_back(pJutta); 30 whoMadeCoffee.push_back(pNico); 31 32 // print all elements 33 for (auto ptr : whoMadeCoffee) 34 cout << *ptr << " "; 35 cout << endl; 36 37 // overwrite a name again 38 *pNico = "Nicolai"; 39 40 // print all elements 41 for (auto ptr : whoMadeCoffee) 42 cout << *ptr << " "; 43 cout << endl; 44 45 // print some internal data 46 cout << "use_count: " << whoMadeCoffee[0].use_count() << endl; 47 48 return 0; 49 }

      程序運行結果如下:

      關于程序邏輯可見下圖:

      關于程序的幾點說明:

      1)對智能指針pNico的拷貝是淺拷貝,所以當我們改變對象“Nico”的值為“Nicolai”時,指向它的指針都會指向新值。

      2)指向對象“Jutta”的有四個指針:pJutta和pJutta的三份被安插到容器內的拷貝,所以上述程序輸出的use_count為4。

      4)shared_ptr本身提供默認內存釋放器(default deleter),調用的是delete,不過只對“由new建立起來的單一對象”起作用。當然我們也可以自己定義內存釋放器,就如上述程序。不過值得注意的是,默認內存釋放器并不能釋放數組內存空間,而是要我們自己提供內存釋放器,如:

      1 shared_ptr pJutta2(new int[10], 2 // deleter (a lambda function) 3 [](int *p) 4 { 5 delete[] p; 6 } 7 );

      或者使用為unique_ptr而提供的輔助函數作為內存釋放器,其內調用delete[]:

      1 shared_ptr p(new int[10], default_delete());

      unique_ptr

      unique_ptr是C++標準庫自C++11起開始提供的類型。它是一種在異常發生時可幫助避免資源泄露的智能指針。一般而言,這個智能指針實現了獨占式擁有概念,意味著它可確保一個對象和其相應資源同一時間只被一個指針擁有。一旦擁有者被銷毀或變成空,或開始擁有另一個對象,先前擁有的那個對象就會被銷毀,其任何相應資源也會被釋放。

      現在,本文最開頭的程序就可以寫成這樣啦:

      1 #include 2 using namespace std; 3 4 int main() 5 { 6 unique_ptr ptr(new int(0)); 7 return 0; 8 }

      智能指針簡單實現

      基于引用計數的智能指針可以簡單實現如下(詳細解釋見程序中注釋):

      1 #include 2 using namespace std; 3 4 template 5 class SmartPtr 6 { 7 public: 8 SmartPtr(T *p); 9 ~SmartPtr(); 10 SmartPtr(const SmartPtr &orig); // 淺拷貝 11 SmartPtr& operator=(const SmartPtr &rhs); // 淺拷貝 12 private: 13 T *ptr; 14 // 將use_count聲明成指針是為了方便對其的遞增或遞減操作 15 int *use_count; 16 }; 17 18 template 19 SmartPtr::SmartPtr(T *p) : ptr(p) 20 { 21 try 22 { 23 use_count = new int(1); 24 } 25 catch (...) 26 { 27 delete ptr; 28 ptr = nullptr; 29 use_count = nullptr; 30 cout << "Allocate memory for use_count fails." << endl; 31 exit(1); 32 } 33 34 cout << "Constructor is called!" << endl; 35 } 36 37 template 38 SmartPtr::~SmartPtr() 39 { 40 // 只在最后一個對象引用ptr時才釋放內存 41 if (--(*use_count) == 0) 42 { 43 delete ptr; 44 delete use_count; 45 ptr = nullptr; 46 use_count = nullptr; 47 cout << "Destructor is called!" << endl; 48 } 49 } 50 51 template 52 SmartPtr::SmartPtr(const SmartPtr &orig) 53 { 54 ptr = orig.ptr; 55 use_count = orig.use_count; 56 ++(*use_count); 57 cout << "Copy constructor is called!" << endl; 58 } 59 60 // 重載等號函數不同于復制構造函數,即等號左邊的對象可能已經指向某塊內存。 61 // 這樣,我們就得先判斷左邊對象指向的內存已經被引用的次數。如果次數為1, 62 // 表明我們可以釋放這塊內存;反之則不釋放,由其他對象來釋放。 63 template 64 SmartPtr& SmartPtr::operator=(const SmartPtr &rhs) 65 { 66 // 《C++ primer》:“這個賦值操作符在減少左操作數的使用計數之前使rhs的使用計數加1, 67 // 從而防止自身賦值”而導致的提早釋放內存 68 ++(*rhs.use_count); 69 70 // 將左操作數對象的使用計數減1,若該對象的使用計數減至0,則刪除該對象 71 if (--(*use_count) == 0) 72 { 73 delete ptr; 74 delete use_count; 75 cout << "Left side object is deleted!" << endl; 76 } 77 78 ptr = rhs.ptr; 79 use_count = rhs.use_count; 80 81 cout << "Assignment operator overloaded is called!" << endl; 82 return *this; 83 }

      測試程序如下:

      1 #include 2 #include "smartptr.h" 3 using namespace std; 4 5 int main() 6 { 7 // Test Constructor and Assignment Operator Overloaded 8 SmartPtr p1(new int(0)); 9 p1 = p1; 10 // Test Copy Constructor 11 SmartPtr p2(p1); 12 // Test Assignment Operator Overloaded 13 SmartPtr p3(new int(1)); 14 p3 = p1; 15 16 return 0; 17 }

      C++智能指針及其簡單實現

      測試結果如下:

      參考資料

      《C++標準庫(第二版)》

      C++中智能指針的設計和使用

      C++

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:【Android 安裝包優化】WebP 應用 ( Android 中使用 libwebp.so 庫編碼 WebP 圖片 )
      下一篇:聊聊DevOps下的測試技術(2)聊聊契約與契約測試
      相關文章
      亚洲av最新在线网址| 亚洲GV天堂GV无码男同| 亚洲国产精品国产自在在线| 亚洲国产区男人本色在线观看| 亚洲精品福利网站| 中文字幕亚洲免费无线观看日本| 久久久久久久尹人综合网亚洲| 亚洲色欲一区二区三区在线观看| 久久久久亚洲av成人无码电影 | 偷自拍亚洲视频在线观看99| 亚洲A∨精品一区二区三区下载| 亚洲人成网站色7799| 亚洲日韩精品无码专区| 亚洲乱色伦图片区小说| 亚洲av成人一区二区三区观看在线 | 亚洲人成77777在线观看网| 67194在线午夜亚洲| 亚洲妇女熟BBW| 亚洲GV天堂无码男同在线观看| 337P日本欧洲亚洲大胆艺术图| 国产亚洲人成在线播放| 亚洲精品NV久久久久久久久久| 亚洲片国产一区一级在线观看| 国产精品亚洲w码日韩中文| 亚洲无人区午夜福利码高清完整版 | 亚洲精品无码mv在线观看网站| 国产成人亚洲综合无码精品 | 亚洲国产精品成人综合色在线| 精品国产亚洲一区二区三区在线观看 | 国产av无码专区亚洲av毛片搜 | 亚洲国产夜色在线观看| 亚洲五月综合网色九月色| 亚洲中文字幕乱码AV波多JI | 麻豆亚洲AV成人无码久久精品 | 亚洲一区二区三区免费视频| 亚洲老熟女五十路老熟女bbw | 亚洲AV无码乱码精品国产| 亚洲熟女乱综合一区二区| 亚洲精品乱码久久久久久按摩 | jlzzjlzz亚洲乱熟在线播放| 亚洲乱码一区二区三区在线观看|