C++11 shared_ptr(智能指針)詳解


要確保用 new 動態分配的內存空間在程序的各條執行路徑都能被釋放是一件麻煩的事情。C++ 11 模板庫的 <memory> 頭文件中定義的智能指針,即 shared _ptr 模板,就是用來部分解決這個問題的。

只要將 new 運算符返回的指針 p 交給一個 shared_ptr 對象“托管”,就不必擔心在哪里寫delete p語句——實際上根本不需要編寫這條語句,托管 p 的 shared_ptr 對象在消亡時會自動執行delete p。而且,該 shared_ptr 對象能像指針 p —樣使用,即假設托管 p 的 shared_ptr 對象叫作 ptr,那么 *ptr 就是 p 指向的對象。

通過 shared_ptr 的構造函數,可以讓 shared_ptr 對象托管一個 new 運算符返回的指針,寫法如下:

shared_ptr<T> ptr(new T);  // T 可以是 int、char、類等各種類型

此后,ptr 就可以像 T* 類型的指針一樣使用,即 *ptr 就是用 new 動態分配的那個對象。

多個 shared_ptr 對象可以共同托管一個指針 p,當所有曾經托管 p 的 shared_ptr 對象都解除了對其的托管時,就會執行delete p

例如下面的程序:

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. class A
  5. {
  6. public:
  7. int i;
  8. A(int n):i(n) { };
  9. ~A() { cout << i << " " << "destructed" << endl; }
  10. };
  11. int main()
  12. {
  13. shared_ptr<A> sp1(new A(2)); //A(2)由sp1托管,
  14. shared_ptr<A> sp2(sp1); //A(2)同時交由sp2托管
  15. shared_ptr<A> sp3;
  16. sp3 = sp2; //A(2)同時交由sp3托管
  17. cout << sp1->i << "," << sp2->i <<"," << sp3->i << endl;
  18. A * p = sp3.get(); // get返回托管的指針,p 指向 A(2)
  19. cout << p->i << endl; //輸出 2
  20. sp1.reset(new A(3)); // reset導致托管新的指針, 此時sp1托管A(3)
  21. sp2.reset(new A(4)); // sp2托管A(4)
  22. cout << sp1->i << endl; //輸出 3
  23. sp3.reset(new A(5)); // sp3托管A(5),A(2)無人托管,被delete
  24. cout << "end" << endl;
  25. return 0;
  26. }

程序的輸出結果如下:
2,2,2
2
3
2 destructed
end
5 destructed
4 destructed
3 destructed

可以用第 14 行及第 16 行的形式讓多個 sharecLptr 對象托管同一個指針。這多個 shared_ptr 對象會共享一個對共同托管的指針的“托管計數”。有 n 個 shared_ptr 對象托管同一個指針 p,則 p 的托管計數就是 n。當一個指針的托管計數減為 0 時,該指針會被釋放。shared_ptr 對象消亡或托管了新的指針,都會導致其原托管指針的托管計數減 1。

第 20、21 行,shared_ptr 的 reset 成員函數可以使得對象解除對原托管指針的托管(如果有的話),並托管新的指針。原指針的托管計數會減 1。

輸出的第 4 行說明,用 new 創建的動態對象 A(2) 被釋放了。程序中沒有寫 delete 語句,而 A(2) 被釋放,是因為程序的第 23 行執行后,已經沒有 shared_ptr 對象托管 A(2),於是 A(2) 的托管計數變為 0。最后一個解除對 A(2) 托管的 shared_ptr 對象會釋放 A(2)。

main 函數結束時,sp1、sp2、sp3 對象消亡,各自將其托管的指針的托管計數減為 0,並且釋放其托管的指針,於是會有以下輸出:
5 destructed
4 destructed
3 destructed

只有指向動態分配的對象的指針才能交給 shared_ptr 對象托管。將指向普通局部變量、全局變量的指針交給 shared_ptr 托管,編譯時不會有問題,但程序運行時會出錯,因為不能析構一個並沒有指向動態分配的內存空間的指針。

注意,不能用下面的方式使得兩個 shared_ptr 對象托管同一個指針:

  1. A* p = new A(10);
  2. shared_ptr <A> sp1(p), sp2(p);

sp1 和 sp2 並不會共享同一個對 p 的托管計數,而是各自將對 p 的托管計數都記為 1(sp2 無法知道 p 已經被 sp1 托管過)。這樣,當 sp1 消亡時要析構 p,sp2 消亡時要再次析構 p,這會導致程序崩潰。


免責聲明!

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



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