深入理解C++智能指針


  什么是智能指針?為什么需要這個東西?

  這需要從C++最早的設計說起,C++為了提高語言的自由程度,允許使用C++的程序員自己控制內存,C++繼承了C語言的指針特性,允許用戶在C++中像C一樣使用指針在堆中開辟一塊較大的內存,但是由於性能的考慮,用戶自己申請的內存需要用戶自己主動釋放。這就導致了有時遺忘釋放時帶來的內存泄漏問題。由此智能指針為了解決這個問題,誕生了。

  C++的智能指針從C++11標准開始由boost引入, 關於boost的智能指針在此不再做介紹。

  以下討論的內容涉及了C++11-C++20中的技術規則。

  在C++中使用智能指針需要包含<memory>頭文件,智能指針作為STL的一部分來提供,所以在使用時需要使用std::命名空間,智能指針標准庫提供了兩個主要類:std::unique_ptr(私有指針)和std::shared_ptr(共享指針)。外加一個輔助類std::weak_ptr,下面一一介紹:

1.私有指針std::unique_ptr

  std::unique_ptr是一個模板類,所以在使用的時候需要輸入一個類型參數,並且標准庫提供了一個內存申請函數用來替代new。std::make_unique.

  

1 #include<memory>
2 
3 int main() {
4 
5     std::unique_ptr<int> p = std::make_unique<int>(12);
6 
7     // 類似於 int*p = new int(12);
8     return 0;
9 }

  當私有智能智能指針超出當前作用域時,智能指針將釋放自己持有的內存。也就是說,上面的p指針,在main函數返回時,釋放所持有的內存。智能指針也可以搭配new來完成內存的申請。

1 #include<memory>
2 
3 int main() {
4 
5 
6     std::unique_ptr<int> p2{ new int(13) };
7    
8     return 0;
9 }

此時,在p2超出作用域時,依然可以自動釋放所持有的內存。

私有指針是不允許被復制的。

以下語法是錯誤的:

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::unique_ptr<int> p2{ new int(13) };
 7 
 8     std::unique_ptr<int> m = p2;  // 錯誤,不允許被復制
 9    
10     return 0;
11 }

但是,可以被移動:

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::unique_ptr<int> p2{ new int(13) };
 7 
 8     std::unique_ptr<int> m = std::move(p2);
 9    
10     return 0;
11 }

私有指針要求,每一塊內存只能有一個對象持有。所以符合移動復制的都是合法的,符合拷貝復制的都是不合法的。

對於智能指針數組,標准庫依然有一套特化來解決。

1 #include<memory>
2 
3 int main() {
4 
5 
6     std::unique_ptr<int[]> p = std::make_unique<int[]>(12);
7    
8     return 0;
9 }

上面的代碼中表示,申請了一塊數組長度為12 int類型的連續內存。數組指針也可以配合new來完成,以達到數組的初始化工作。

1 #include<memory>
2 
3 int main() {
4 
5 
6     std::unique_ptr<int[]> p{ new int[12]{12,23,45,67} };
7    
8     return 0;
9 }

2.共享指針std::shared_ptr

共享指針的使用方式和私有指針相似,但是共享指針是允許被復制的。

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::shared_ptr<int> p = std::make_shared<int>(12);
 7 
 8     auto s = p;  // ok
 9    
10     return 0;
11 }

共享指針的釋放條件是:當所有持有該內存的指針對象全部消亡以后,釋放該內存。

對於智能指針訪問內部數據的方法是,該對象重載了*和->操作符。

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     std::shared_ptr<int> p = std::make_shared<int>(12);
 7 
 8     int m = *p;    // 讀取p的內存值
 9 
10     int* s = p.get();  // 獲取p的內存的地址
11    
12     return 0;
13 }

私有智能指針允許將內存管理權限交於程序員自己管理。

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     auto p = std::make_unique<int>(12);
 7 
 8     int* s = p.release();   // 此時智能指針對象在消亡時,不再主動釋放內存。
 9 
10     return 0;
11 }

共享指針由於存在可能被多個對象持有的情況,所以不支持內存權限轉移。

3. std::waek_ptr

 1 #include<memory>
 2 
 3 int main() {
 4 
 5 
 6     auto p = std::make_shared<int>(12);
 7     
 8     std::weak_ptr<int> wk(p);
 9     if (!wk.lock()) {
10         // 對象已經釋放。
11     }
12     wk.reset(); // 釋放持有的智能指針對象。(注意:不是釋放持有的智能指針的內存,而是釋放持有的p對象的引用。)
13 
14     return 0;
15 }

std::waek_ptr在使用時的主要用途是用來判斷一個共享指針是否已經釋放,對於智能指針的原理,每個人都有自己的一套見解,對於智能指針的原理,這就涉及到源碼解析,由於篇幅過大,在此不再細說。

有幾點是需要注意的:

1.盡量避免智能指針與普通指針的混用。(你要是想騷,那也沒辦法)。

2.避免重寫智能指針的內存管理類。(同上)。

3.在團隊項目上應與團隊成員商議,是否采用智能指針。

智能指針的出現,是為了更方便,而不是為了更騷,更復雜,一切都應該建立在:此方法能夠讓我更舒服的且高效的工作。

 

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM