一、shared_ptr學習
1.shared_ptr和weak_ptr 基礎概念
- shared_ptr與weak_ptr智能指針均是C++ RAII的一種應用,可用於動態資源管理
- shared_ptr基於“引用計數”模型實現,多個shared_ptr可指向同一個動態對象,並維護了一個共享的引用計數器,記錄了引用同一對象的shared_ptr實例的數量。當最后一個指向動態對象的shared_ptr銷毀時,會自動銷毀其所指對象(通過delete操作符)。
- shared_ptr的默認能力是管理動態內存,但支持自定義的Deleter以實現個性化的資源釋放動作。
- weak_ptr用於解決“引用計數”模型循環依賴問題,weak_ptr指向一個對象,並不增減該對象的引用計數器
2.shared_ptr的基本操作
#include <memory> #include <iostream> struct Foo { Foo() { std::cout << "Foo...\n"; } ~Foo() { std::cout << "~Foo...\n"; } }; struct D { //刪除p所指向的Foo對象 void operator()(Foo* p) const { std::cout << "Call delete for Foo object...\n"; delete p; } }; int main() { // constructor with no managed object std::shared_ptr<Foo> sh1; // constructor with object std::shared_ptr<Foo> sh2(new Foo); std::shared_ptr<Foo> sh3(sh2); std::cout << sh2.use_count() << '\n'; std::cout << sh3.use_count() << '\n'; //constructor with object and deleter std::shared_ptr<Foo> sh4(new Foo, D()); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
構造方法:
1.通過make_shared函數構造
auto s_s = make_shared(“hello”);
2.通過原生指針構造
int* pNode = new int(5);
shared_ptr s_int(pNode);
//獲取原生指針
int* pOrg = s_int.get();
3.通過賦值函數構造shared_ptr
4.重載的operator->, operator *,以及其他輔助操作如unique()、use_count(), get()等成員方法。
3 實驗智能指針引用計數,增加和減少的規律
實驗的主要內容有:
1.shared_ptr變量在生命周期中銷毀后,引用計數是否減1?
2.shared_ptr作為函數參數,分為傳值和傳引用,引用計數如何變化?
2.函數返回值為shared_ptr類型時,引用計數是否會變化?
帶着這幾個問題,我們來看下代碼.
#include <iostream> #include <memory> using namespace std; void Func1(shared_ptr<int> a) { cout<<"Enter Func1"<<endl; cout<<"Ref count: "<<a.use_count()<<endl; cout<<"Leave Func1"<<endl; } shared_ptr<int> Func2(shared_ptr<int>& a) { cout<<"Enter Func2"<<endl; cout<<"Ref count: "<<a.use_count()<<endl; cout<<"Leave Func2"<<endl; return a; } int main() { //構造一個指向int類型對象的指針aObj1,引用計數+1 shared_ptr<int> aObj1(new int(10)); cout<<"Ref count: "<<aObj1.use_count()<<endl; { //同aObj1,不過由於生存周期在括號內,所以aObj2會被銷毀 shared_ptr<int> aObj2 = aObj1; cout<<"Ref count: "<<aObj2.use_count()<<endl;//引用計數-1 } //在調用函數時,參數為shared_ptr類型,參數為傳值類型,智能指針引用計數+1 Func1(aObj1); //在調用函數時,參數為shared_ptr類型,參數為傳引用類型,智能指針引用計數不變 Func2(aObj1); shared_ptr<int> aObj3 = Func2(aObj1);//引用計數+1 cout<<"Ref count:"<<aObj3.use_count()<<endl; return 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
運行結果如下:
有效的掌握好智能指針的引用計數的變化規律,才能把程序寫的更好.
4. shared_ptr的應用場景以及使用注意事項
4.1 對象之間“共享數據”,對象創建與銷毀“分離”
4.2 放入容器中的動態對象,使用shared_ptr包裝,比unique_ptr更合適
4.3 管理“動態數組”時,需要制定Deleter以使用delete[]操作符銷毀內存,因為shared_ptr並沒有針對數組的特化版本(unique_ptr有針對數組的特化版本)
5.shared_ptr的線程安全問題
- 同一個shared_ptr被多個線程讀,是線程安全的;
- 同一個shared_ptr被多個線程寫,不是 線程安全的;
- 共享引用計數的不同的shared_ptr被多個線程寫,是線程安全的。
對於第三點,我們一般采用:
對於線程中傳入的外部shared_ptr對象,在線程內部進行一次新的構造,例如: sharedptr AObjTmp = outerSharedptrObj;
二、weak_ptr學習
我們先搞清楚,weak_ptr為什么出現,或者說它是為了解決什么問題而存在的(存在即合理),哈哈
class Parent { public: shared_ptr<Child> child; }; class Child { public: shared_ptr<Parent> parent; }; shared_ptr<Parent> pA(new Parent); shared_ptr<Child> pB(new Child); pA->child = pB; pB->parent = pA;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在Parent類中存儲了指向Child類對象的智能指針成員變量,而在Child類中也存儲了指向Parent類對象的智能指針成員變量,如此就會造成環形引用,這個成因在C++中很好解釋.
要解決環形引用的問題,沒有特別好的辦法,一般都是在可能出現環形引用的地方使用weak_ptr來代替shared_ptr。說到了weak_ptr,那下面就接着總結weak_ptr吧。
下面我們來一起學習下weak_ptr這個東東
weak_ptr指向shared_ptr指針指向的對象的內存,卻並不擁有該內存。
但是,使用weak_ptr成員lock,則可返回其指向內存的一個shared_ptr對象,且在所指對象內存已經無效時,返回指針空值(nullptr)。由於weak_ptr是指向shared_ptr所指向的內存的,所以,weak_ptr並不能獨立存在。
#include <iostream> #include <memory> using namespace std; void Check(weak_ptr<int> &wp) { shared_ptr<int> sp = wp.lock(); // 重新獲得shared_ptr對象 if (sp != nullptr) { cout << "The value is " << *sp << endl; } else { cout << "Pointer is invalid." << endl; } } int main() { shared_ptr<int> sp1(new int(10)); shared_ptr<int> sp2 = sp1; weak_ptr<int> wp = sp1; // 指向sp1所指向的內存 cout << *sp1 << endl; cout << *sp2 << endl; Check(wp); sp1.reset(); cout << *sp2 << endl; Check(wp); sp2.reset(); Check(wp); system("pause"); return 0; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
學習編程最好的方式就是一步步的跟蹤去調試.
借鑒上面的代碼,我們在使用weak_ptr時也要當心,時刻需要判斷weak_ptr對應的shared_ptr是否為空,weak_ptr並不會增加shared_ptr的引用計數.
另附一篇地址,講解為何不同的shared_ptr對象可以被多線程同時修改(即使這些shared_ptr對象管理着同一個對象的指針)
https://blog.csdn.net/jiangfuqiang/article/details/8292906