一、什麼是智能指針 一般來講C++中對於指針指向的對象需要使用new主動分配堆空間,在使用結束後還需要主動調用delete釋放這個堆空間。為了使得自動、異常安全的對象生存期管理可行,就出現了智能指針這個概念。簡單來看智能指針是 RAII(Resource Acquisition Is Initial ...
一、什麼是智能指針
一般來講C++中對於指針指向的對象需要使用new主動分配堆空間,在使用結束後還需要主動調用delete釋放這個堆空間。為了使得自動、異常安全的對象生存期管理可行,就出現了智能指針這個概念。簡單來看智能指針是 RAII(Resource Acquisition Is Initialization,資源獲取即初始化) 機制對普通指針進行的一層封裝。這樣使得智能指針的行為動作像一個指針,本質上卻是一個對象,這樣可以方便管理一個對象的生命周期。
智能指針作用總結:
- 處理記憶體泄漏。
- 處理空懸指針的問題。
- 處理異常造成的記憶體泄露。
註:智能指針和原生指針不要混用,使用不當可能會導致程式異常;
二、智能指針有哪些
智能指針(動態記憶體管理)頭文件 <memroy>
- std::unique_ptr:擁有獨有對象所有權語義的智能指針(cplusplus,C++參考手冊)
- std::shared_ptr:擁有共用對象所有權語義的智能指針(cplusplus,C++參考手冊)
- std::week_ptr:到 std::shared_ptr 所管理對象的弱引用(cplusplus,C++參考手冊)
- std::auto_ptr(C++11起棄用):擁有嚴格對象所有權語義的智能指針(cplusplus,C++參考手冊)
三、獨占式智能指針(std::unique_ptr)
1 class SmartPointer 2 { 3 public: 4 SmartPointer() 5 { 6 cout << "SmartPointer::SmartPointer()" << endl; 7 } 8 ~SmartPointer() 9 { 10 11 cout << "SmartPointer::~SmartPointer()" << endl; 12 } 13 };
- 獨占式:
- unique_ptr擁有所指向(管理)的資源、對象的所有權,即不能被其他unique_ptr所指;
- unique_ptr不能進行賦值或拷貝操作;
unique_ptr<SmartPointer> ptest1(new SmartPointer("空格")); // unique_ptr<SmartPointer> ptest2 = ptest1; //此時編譯報錯
- 但可以使用std::move或者relase()方法將源unique_ptr 指針指向資源所有權轉向新unique_ptr;
unique_ptr<SmartPointer> ptest1(new SmartPointer("空格")); cout << "ptest1 location: " << ptest1.get() << endl; unique_ptr<SmartPointer> ptest2 = move(ptest1); //將ptest1指向對象所有權轉移給ptest2,ptest1置空為NULL cout << "ptest2 location: " << ptest2.get() << endl; unique_ptr<SmartPointer> ptest3(ptest2.release()); //將ptest2指向對象所有權轉移給ptest3,ptest2置空為NULL cout << "ptest3 location: " << ptest2.get() << endl;
結果為:
unique_ptr
本身擁有的幾個主要方法:
- get() 方法:獲取其保存的原生指針,儘量不要使用;
- release() 方法:釋放所管理指針的所有權,返回原生指針。但並不銷毀原生指針;
- reset() 方法:釋放並銷毀原生指針。如果參數為一個新指針,將管理這個新指針;
- bool() 方法:判斷是否擁有指針;
unique_ptr<SmartPointer> ptest1(new SmartPointer()); ptest1.reset(new SmartPointer()); //釋放銷毀原有對象,持有新對象 ptest1.reset(); //直接釋放銷毀原對象 ptest1 = nullptr; //同上
四、共用式智能指針(std::shared_ptr)
- 共用式
共用權,多個shared_ptr同時擁有一個原生指針(記憶體)的所有權,最後一個擁有者負責原生指針的釋放和銷毀;
std::shared_ptr<SmartPointer> pTest1(new SmartPointer()); std::shared_ptr<SmartPointer> pTest2 = pTest1; //編譯正常,允許所有權的共用 cout << "pTest1 location: " << pTest1.get() << endl; cout << "pTest2 location: " << pTest2.get() << endl; shared_ptr<SmartPointer> pTest3 = make_shared<SmartPointer>();//新建共用指針方法,make_shared效果類似new pTest2 = pTest3; cout << "pTest3 location: " << pTest3.get() << endl; cout << "pTest2 location: " << pTest2.get() << endl;
結果:
- 計數器
共用指針類中包括一個成員函數用來記住所管理的記憶體當前有多少個指針指向它;
use_count()方法可以獲取指向對象的shared_ptr個數;
shared_ptr<string> pStr_1(new string("霜之哀傷")); shared_ptr<string> pStr_2 = make_shared<string>("火之高興"); auto pStr_3 = pStr_1; //此時指向“霜之哀傷”數量為2,“火之高興”為1; cout << *pStr_1 << " : " << pStr_1.use_count() << "\t" << *pStr_2 << " : " << pStr_2.use_count() << endl; pStr_3 = pStr_2; //此時指向“霜之哀傷”數量為1,“火之高興”為2; cout << *pStr_1 << " : " << pStr_1.use_count() << "\t" << *pStr_2 << " : " << pStr_2.use_count() << endl;
結果:
- shared_ptr擁有的幾個主要方法:
- get() 方法:獲取其保存的原生指針,儘量不要使用;
- bool() 方法:判斷是否擁有指針;
- reset() 方法:釋放並銷毀原生指針。如果參數為一個新指針,將管理這個新指針;
- unique() 方法:如果引用計數為 1(即對象所有權唯一),則返回 true,否則返回 false;
- use_count() 方法:返回引用計數的大小;
shared_ptr<SmartPointer> pTest1(new SmartPointer()); cout << pTest1.unique() << endl; //此時為true; shared_ptr<SmartPointer> pTest2 = pTest1; cout << pTest1.unique() << endl; //此時為false pTest1.reset(new SmartPointer()); //釋放銷毀原有對象,持有新對象 pTest1.reset(); //直接釋放銷毀原對象 pTest1 = nullptr; //同上
結果:
五、輔助指針/弱指針(std::weak_ptr)
class B; class A { public: A() { cout << "A::A()" << endl; } ~A() { cout << "A::~A()" << endl; } shared_ptr<B> pB; }; class B { public: B() { cout << "B::B()" << endl; } ~B() { cout << "B::~B()" << endl; } shared_ptr<A> pA; };
weak_ptr是為了輔助shared_ptr所引入的弱引用智能指針,主要是為瞭解決shared_ptr中迴圈引用(對象A持有對象B,對象B持有對象A,此時兩個對象的引用計數均為2;在跳出作用範圍時,兩個對象引用計數均減1但還有1,導致跳出作用範圍後兩個對象的資源沒有釋放銷毀,產生記憶體泄漏)的問題。
shared_ptr<A> pA(new A()); //新建A類對象 shared_ptr<B> pB(new B()); //新建B類對象 pA->pB = pB; //A類中持有B類 pB->pA = pA; //B類中持有A類 //此時構成了迴圈引用 cout << pA.use_count() << endl; cout << pB.use_count() << endl;
結果:
迴圈引用在跳出作用範圍時未調用類A與類B析構函數,導致記憶體泄漏,此時可以將類內的shared_ptr改為weak_ptr可以避免;
weak_ptr不能直接使用原生指針構造,可以使用一個shared_ptr和另一個weak_ptr進行構造;
weak_ptr
擁有的幾個主要方法
- expired() 方法:判斷所指向的原生指針是否被釋放,如果被釋放了返回 true,否則返回 false;
- use_count() 方法:返回原生指針的引用計數;
- lock() 方法:返回 shared_ptr,如果原生指針沒有被釋放,則返回一個非空的 shared_ptr,否則返回一個空的 shared_ptr;
- reset() 方法:將本身置空;
shared_ptr<A> pA(new A()); weak_ptr<A> weak_pA = pA; //弱指針,不增加引用計數 cout << "A引用計數:" << weak_pA.use_count() << endl; shared_ptr<A> pA_1 = weak_pA.lock(); //此時又一個shared_ptr指向原生指針,計數加1 cout << "A引用計數:" << weak_pA.use_count() << endl; cout << weak_pA.expired() << endl; //銷毀原生指針 pA.reset(); pA_1.reset(); cout << weak_pA.expired() << endl; weak_pA.reset(); //置空weak_ptr
結果:
參考:
https://www.cnblogs.com/corineru/p/10895249.html
https://www.cnblogs.com/TenosDoIt/p/3456704.html
https://zhuanlan.zhihu.com/p/436290273
https://www.csdn.net/tags/MtTaEg0sMTkwMTctYmxvZwO0O0OO0O0O.html
搜索
複製