一、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的引用計數.