天天看點

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

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. shared_ptr<A> sp1(new A(2)); //A(2)由sp1托管,
  13. shared_ptr<A> sp2(sp1); //A(2)同時交由sp2托管
  14. shared_ptr<A> sp3;
  15. sp3 = sp2; //A(2)同時交由sp3托管
  16. cout << sp1->i << "," << sp2->i <<"," << sp3->i << endl;
  17. A * p = sp3.get(); // get傳回托管的指針,p 指向 A(2)
  18. cout << p->i << endl; //輸出 2
  19. sp1.reset(new A(3)); // reset導緻托管新的指針, 此時sp1托管A(3)
  20. sp2.reset(new A(4)); // sp2托管A(4)
  21. cout << sp1->i << endl; //輸出 3
  22. sp3.reset(new A(5)); // sp3托管A(5),A(2)無人托管,被delete
  23. cout << "end" << endl;
  24. return 0;
  25. }

程式的輸出結果如下:

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,并且釋放其托管的指針,于是會有以下輸出:

隻有指向動态配置設定的對象的指針才能交給 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,這會導緻程式崩潰。

Talk is cheap. Show me the code