本文根據(jù)眾多互聯(lián)網(wǎng)博客內(nèi)容整理后形成宰衙,引用內(nèi)容的版權(quán)歸原始作者所有齐邦,僅限于學(xué)習(xí)研究使用物咳,不得用于任何商業(yè)用途。
智能指針主要有:unique_ptr, shared_ptr, weak_ptr赋咽。這3種指針組件就是采用了boost里的智能指針方案。很多有用過boost智能指針的朋友吨娜,很容易地就能發(fā)現(xiàn)它們之間的關(guān)間:
std | boost | 功能說明 |
---|---|---|
unique_ptr | scoped_ptr | 獨占指針對象脓匿,并保證指針?biāo)笇ο笊芷谂c其一致 |
shared_ptr | shared_ptr | 可共享指針對象,可以賦值給shared_ptr或weak_ptr宦赠。指針?biāo)笇ο笤谒械南嚓P(guān)聯(lián)的shared_ptr生命周期結(jié)束時結(jié)束陪毡,是強引用。 |
weak_ptr | weak_ptr | 它不能決定所指對象的生命周期勾扭,引用所指對象時毡琉,需要lock()成shared_ptr才能使用。 |
C++11將boost里的這一套納入了標(biāo)準(zhǔn)妙色。
如下為示例代碼:
//文件 test-1.cpp
#include <memory>
#include <iostream>
using namespace std;
int main(){
unique_ptr<int> up1(new int(11));
unique_ptr<int> up2 = up1; //! 編譯時會出錯 [1]
cout << *up1 << endl;
unique_ptr<int> up3 = move(up1); //! [2]
cout << *up3 << endl;
if (up1)
cout << *up1 << endl;
up3.reset(); //! [3]
up1.reset();
shared_ptr<string> sp1(make_shared<string>("Hello"));
shared_ptr<string> sp2 = sp1;
cout << "*sp1:" << *sp1 << endl;
cout << "*sp2:" << *sp2 << endl;
sp1.reset();
cout << "*sp2:" << *sp2 << endl;
weak_ptr<string> wp = sp2; //! [4]
cout << "*wp.lock():" << *wp.lock() << endl;
sp2.reset();
cout << "*wp.lock():" << *wp.lock() << endl; //! 運行時會出錯
return 0;
}
//編譯命令: g++ -std=c++11 test-1.cpp
[1]: unique_ptr是禁止復(fù)制賦值的桅滋,始終保持一個 unique_ptr管理一個對象。
[2]: unique_ptr雖然不能賦值身辨,但可以通過 move()函數(shù)轉(zhuǎn)移對象的所有權(quán)丐谋。一旦被 move()了,原來的 up1則不再有效了煌珊。
[3]: reset()可以讓 unique_ptr提前釋放指針笋鄙。
[4]: 由 shared_ptr構(gòu)造一個 weak_ptr。
unique_ptr
unique_ptr(定義在中)提供了一種嚴(yán)格的語義上的所有權(quán)怪瓶,
- 擁有它所指向的對象萧落。
- 無法進行復(fù)制構(gòu)造践美,也無法進行復(fù)制賦值操作(譯注:也就是對其無法進行復(fù)制,我們無法得到指向同一個對象的兩個unique_ptr)找岖,但是可以進行移動構(gòu)造和移動賦值操作陨倡。
- 保存指向某個對象的指針,當(dāng)它本身被刪除釋放的時候(例如许布,離開某個作用域)兴革,會使用給定的刪除器(deleter)刪除釋放它指向的對象。
unique_ptr的使用能夠包括:
- 為動態(tài)申請的內(nèi)存提供異常安全
- 將動態(tài)申請內(nèi)存的所有權(quán)傳遞給某個函數(shù)
- 從某個函數(shù)返回動態(tài)申請內(nèi)存的所有權(quán)
- 在容器中保存指針
“所有auto_ptr應(yīng)該已經(jīng)具有的(但是我們無法在C++98中實現(xiàn)的)功能”
unique_ptr十分依賴于右值引用和移動語義蜜唾。
下面是一段傳統(tǒng)的會產(chǎn)生不安全的異常的代碼:
X* f()
{
X* p = new X;
// 做一些事情 – 可能會拋出某個異常
return p;
}
解決方法是杂曲,用一個unique_ptr 來管理這個對象的所有權(quán),由其進行這個對象的刪除釋放工作:
X* f()
{
unique_ptr p(new X); // 或者使用{new X}袁余,但是不能 = new X
// 做一些事情 – 可能會拋出異常
return p.release();
}
現(xiàn)在擎勘,如果程序執(zhí)行過程中拋出了異常,unique_ptr就會(毫無疑問地)刪除釋放它所指向的對象颖榜,這是最基本的RAII棚饵。但是,除非我們真的需要返回一個內(nèi)建的指針掩完,我們可以返回一個unique_ptr噪漾,讓事情變得更好。
unique_ptr f()
{
unique_ptr p(new X); // 或者使用{new X}且蓬,但是不能 = new X
//做一些事情 – 可能會拋出異常
return p; // 對象的所有權(quán)被傳遞出f()
}
現(xiàn)在我們可以這樣使用函數(shù)f():
void g()
{
unique_ptr q = f(); // 使用移動構(gòu)造函數(shù)(move constructor)
q->memfct(2); // 使用q
X x = *q; // 復(fù)制指針q所指向的對象
// …
} // 在函數(shù)退出的時候欣硼,q以及它所指向的對象都被刪除釋放
unique_ptr擁有“移動意義(move semantics)”,所以我們可以使用函數(shù)f() 返回的右值對q進行初始化恶阴,這樣就簡單地將所有權(quán)傳遞給了q诈胜。
在那些要不是為了避免不安全的異常問題(以及為了保證指針?biāo)赶虻膶ο蠖急徽_地刪除釋放),我們不可以使用內(nèi)建指針的情況下存淫,我們可以在容器中保存unique_ptr以代替內(nèi)建指針:
vector<unique_ptr<string>> vs { new string{“Doug”},
new string{“Adams”} };
unique_ptr可以通過一個簡單的內(nèi)建指針構(gòu)造完成耘斩,并且與內(nèi)建指針相比,兩者在使用上的差別很小桅咆。特殊情況下括授,unique_ptr并不提供任何形式的動態(tài)檢查(?)。
shared_ptr 與 weak_ptr
std::weak_ptr 是一種智能指針岩饼,它對被 std::shared_ptr 管理的對象存在非擁有性(“弱”)引用荚虚。在訪問所引用的對象前必須先轉(zhuǎn)換為 std::shared_ptr。
std::weak_ptr 用來表達(dá)臨時所有權(quán)的概念:當(dāng)某個對象只有存在時才需要被訪問籍茧,而且隨時可能被他人刪除時版述,可以使用 std::weak_ptr 來跟蹤該對象。需要獲得臨時所有權(quán)時寞冯,則將其轉(zhuǎn)換為 std::shared_ptr渴析,此時如果原來的 std::shared_ptr 被銷毀晚伙,則該對象的生命期將被延長至這個臨時的 std::shared_ptr 同樣被銷毀為止。
此外俭茧,std::weak_ptr 還可以用來避免 std::shared_ptr 的循環(huán)引用咆疗。
如下面的示例:
shared_ptr<string> s1(new string);
shared_ptr<string> s2 = s1;
weak_ptr<string> w1 = s2;
在內(nèi)存中:
s1, s2, w1 都指向一個 ptr_manage 的對象。
在該對象中有 shared_ref_count與 weak_ref_count兩個域分別記錄引用它的 shared_pt與 weak_ptr的個數(shù)母债。這個很容易辦到午磁,只要在復(fù)制構(gòu)造與賦值函數(shù)中對相當(dāng)?shù)匾弥颠M行加1,在析構(gòu)中減1即可毡们。ptr_manage中的 ptr域存放真正的對象指針地址迅皇。
當(dāng) shared_ref_cnt被減為0時,自動釋放ptr指針?biāo)赶虻膶ο?/strong>衙熔。
當(dāng) shared_ref_cnt與 weak_ref_cn都變成0時登颓,才釋放ptr_manage對象。
如此以來青责,只要有相關(guān)聯(lián)的 shared_ptr存在挺据,對象就存在取具。weak_ptr不影響對象的生命周期脖隶。當(dāng)用 weak_ptr訪問對象時,對象有可能已被釋放了暇检,要先 lock()产阱。
當(dāng)執(zhí)行:
s1.reset()
此時:
shared_ref_cnt由2減成了1。
再執(zhí)行:
s2.reset()
此時:
shared_ref_cnt已被減到0了块仆,ptr所對應(yīng)的object已被釋放构蹬,ptr被清0。此時悔据,ptr_manage依舊保留庄敛。因為 w1還需要引用它。
在最后科汗,w1也析構(gòu)了的時候:
ptr_manage中的 weak_ref_cnt被減成0藻烤,最后連 ptr_manage都釋放了。
參考資料
C++11中的智能指針
【c++11FAQ】unique_ptr
std::weak_ptr | cppreference.com