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對象或是沒有析構函數的類時,應當為其傳遞合适的删除器
避免形成指針循環引用
循環引用