shared_ptr的使用
分配內存
- make_shared
//make_shared<int>分配一塊int類型大小的內存,並值初始化爲100 //返回值是shared_ptr類型,因此可以直接賦值給sp shared_ptr<int> sp = make_shared<int>(100);
-
new
接受指針參數的只能指針構造函數是explicit的,因此,我們不能將一個內置指針隱式轉化爲一個只能指針,必須使用直接初始化形式
//錯誤! 不會進行隱式轉換,類型不符合 shared_ptr<int> sp1 = new int(100); //正確,直接初始化調用構造函數 shared_ptr<int> sp2(new int(100000));
shared_ptr的操作
分配好了內存空間,我們就可以使用shared_ptr定義的操作了
-
p.get()
返回p保存的指針 -
swap(p,q)
交換p、q中保存的指針 -
shared_ptr<T> p(q)
p是q的拷貝,它們指向同一塊內存,互相關聯 -
p = q
用q爲p賦值,之後p、q指向同一塊內存,q引用計數+1,p(原來內存空間的)引用計數-1 -
p.use_count()
返回與p共享對象的智能指針數量 -
shared_ptr<T> p(q,d)
q是一個可以轉換爲T*的指針,d是一個可調用對象(作爲刪除器),p接管q所指對象的所有權,用刪除器d代替delete釋放內存 -
p.reset()
將p重置爲空指針 -
p.reset(p)
將p重置爲p(的值) -
p.reset(p,d)
將p重置爲p(的值)並使用d作爲刪除器
shared_ptr 關聯與獨立
多個共享指針指向同一個空間,它們的關係可能是關聯(我們所期望的正常關係)或是獨立的(一種錯誤狀態)
shared_ptr<int> sp1(new int(10));
shared_ptr<int> sp2(sp1), sp3;
sp3 = sp1;
//一個典型的錯誤用法
shared_ptr<int> sp4(sp1.get());
cout << sp1.use_count() << " " << sp2.use_count() << " "
<< sp3.use_count() << " " << sp4.use_count() << endl;
//輸出 3 3 3 1
sp1,sp2,sp3是相互關聯的共享指針,共同控制所指內存的生存期,sp4雖然指向同樣的內存,卻是與sp1,sp2,sp3獨立的,sp4按自己的引用計數來關聯內存的釋放。
只有用一個shared_ptr爲另一個shared_ptr賦值時,纔將這連個共享指針關聯起來,直接使用地址值會導致各個shared_ptr獨立。
向shared_ptr傳遞刪除器
有時候我們需要用智能指針管理非new的對象,或者是沒有析構函數的類,由於shared_ptr默認使用delete來釋放內存並執行析構函數,對於以上的兩種情況是不適用的,所以我們要傳遞特別的刪除器
刪除器必須接受單個類型爲 T* 的參數
//沒有析構函數的類 struct MyStruct { int *p; MyStruct():p(new int(10)) { } //構造函數中申請了一塊內存 //用裸指針管理,不用時需要手動釋放 }; void main() { //st是局部的對象,存放在棧區 //並非由new申請,不可用delete釋放內存 MyStruct st; //一個作用域 { shared_ptr<MyStruct> sp(&st, [](MyStruct *ptr) { delete(ptr->p); ptr->p = nullptr; cout << "destructed." << endl; }); } // 離開作用域,調用傳遞的刪除器釋放sp所指的內存空間 }
對於以上這個例子,首先不可以用delete來釋放局部對象,然後MyStruct也沒有析構函數來釋放申請的空間,所以向管理它的shared_ptr傳遞一個刪除器來做這兩件事。
shared_ptr的陷阱
不要寫出獨立的shared_ptr
關於獨立的shared_ptr的意思及危害上面已經說出,遵守下面幾點來避免這個錯誤
- 不要與裸指針混用
//錯誤場景1 int *x(new int(10)); shared_ptr<int> sp1(x); shared_ptr<int> sp2(x); //雖然sp1、sp2都指向x所指的內存,但他們是獨立的, //會在其他shared_ptr還在使用內存的情況下就釋放掉內存 //失去了設計共享指針的意義 //同時,使用裸指針x本身也是很危險的,x隨時可能變成空懸指針而無從知曉
//錯誤場景2 //函數接受一個共享指針參數 void func(shared_ptr<int> sp); int *x(new int(10)); //創建了一個指向x指針所指內存的共享指針,引用計數爲1,是引用這塊內存的唯一共享指針 func(shared_ptr<int> (x)); //離開函數即離開共享指針的作用域,這塊內存即被刪除
- 不要用p.get()的返回值爲shared_ptr賦值
shared_ptr<int> sp1(new int(10)); //sp2與sp1獨立 shared_ptr<int> sp2(sp1.get()),sp3; //sp3與sp1獨立 sp.reset(sp1.get());
謹慎使用p.get()的返回值
p.get()的返回值就相當於一個裸指針的值,不合適的使用這個值,上述陷阱的所有錯誤都有可能發生,遵守以下幾個約定
-
不要保存p.get()的返回值
無論是保存爲裸指針還是shared_ptr都是錯誤的
保存爲裸指針不知什麼時候就會變成空懸指針
保存爲shared_ptr則產生了獨立指針 -
不要delete p.get()的返回值
會導致對一塊內存delete兩次的錯誤
記得向shared_ptr傳遞刪除器
如果用shared_ptr管理非new對象或是沒有析構函數的類時,應當爲其傳遞合適的刪除器