引入智能指針的背景(目的)
智能指針的引入主要爲了解決程序員在使用new之後該釋放(delete)內存的地方沒釋放(delete),要確保用 new 動態分配的內存空間在程序的各條執行路徑都能被釋放是一件麻煩的事情。C++ 11 模板庫的 <memory> 頭文件中定義的智能指針,即 shared _ptr 模板,就是用來部分解決這個問題的。
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
。
示例代碼
#include <iostream>
#include <memory>
using namespace std;
class A
{
public:
char m_c;
A(char c) : m_c(c) {}
~A() { cout<< m_c << " destrructed" << endl; }
};
int main()
{
shared_ptr<A> sp1(new A('a')); //A(a)由sp1託管,也就是A(a)的所有權歸sp1
shared_ptr<A> sp2(sp1); //A('a')同時交由sp2託管
shared_ptr<A> sp3 = sp2; //A('a')同時交由sp3託管
cout<<"1-->"<<sp1->m_c<<","<<sp2->m_c<<","<<sp3->m_c<<endl;
A* pa = sp3.get(); //get()返回託管的指針, pa 指向 A('a')
cout<<"2-->"<<pa->m_c <<endl;
sp1.reset(new A('b')); //reset導致託管新的指針,此時sp1託管A('b')
sp2.reset(new A('c')); //sp2託管A('c')
sp3.reset(new A('d')); //sp3託管A('d'),A('a')無人託管,被delete
cout<<"3-->"<<sp1->m_c <<","<< sp2->m_c <<","<< sp3->m_c <<endl;
return 0;
}
運行結果
可以讓多個 sharecLptr 對象託管同一個指針。這多個 shared_ptr 對象會共享一個對共同託管的指針的“託管計數”。有 n 個 shared_ptr 對象託管同一個指針 p,則 p 的託管計數就是 n。當一個指針的託管計數減爲 0 時,該指針會被釋放。shared_ptr 對象消亡或託管了新的指針,都會導致其原託管指針的託管計數減 1。shared_ptr 的 reset 成員函數可以使得對象解除對原託管指針的託管(如果有的話),並託管新的指針。原指針的託管計數會減 1。
注意
只有指向動態分配的對象的指針才能交給 shared_ptr 對象託管。將指向普通局部變量、全局變量的指針交給 shared_ptr 託管,編譯時不會有問題,但程序運行時會出錯,因爲不能析構一個並沒有指向動態分配的內存空間的指針。
不能用下面的方式使得兩個 shared_ptr 對象託管同一個指針:
A* p = new A(66);
shared_ptr <A> sp1(p), sp2(p);
sp1 和 sp2 並不會共享同一個對 p 的託管計數,而是各自將對 p 的託管計數都記爲 1(sp2 無法知道 p 已經被 sp1 託管過)。這樣,當 sp1 消亡時要析構 p,sp2 消亡時要再次析構 p,這會導致程序崩潰。