詳解C++中shared_ptr的使用教程
shared_ptr是一種智能指針(smart pointer)。shared_ptr的作用有如同指針,但會(huì)記錄有多少個(gè)shared_ptrs共同指向一個(gè)對(duì)象。
這便是所謂的引用計(jì)數(shù)(reference counting)。一旦最后一個(gè)這樣的指針被銷(xiāo)毀,也就是一旦某個(gè)對(duì)象的引用計(jì)數(shù)變?yōu)?,這個(gè)對(duì)象會(huì)被自動(dòng)刪除。這在非環(huán)形數(shù)據(jù)結(jié)構(gòu)中防止資源泄露很有幫助。
auto_ptr由于它的破壞性復(fù)制語(yǔ)義,無(wú)法滿(mǎn)足標(biāo)準(zhǔn)容器對(duì)元素的要求,因而不能放在標(biāo)準(zhǔn)容器中;如果我們希望當(dāng)容器析構(gòu)時(shí)能自動(dòng)把它容納的指針元素所指的對(duì)象刪除時(shí),通常采用一些間接的方式來(lái)實(shí)現(xiàn),顯得比較繁瑣。boost庫(kù)中提供了一種新型的智能指針shared_ptr,它解決了在多個(gè)指針間共享對(duì)象所有權(quán)的問(wèn)題,同時(shí)也滿(mǎn)足容器對(duì)元素的要求,因而可以安全地放入容器中。
總結(jié)下幾個(gè)使用shared_ptr需要注意的問(wèn)題:
一. 相互引用鏈
class C; class B : public std::enable_shared_from_this<B> { public: ~B(){ cout << "~B" << endl; } void SetPC(std::shared_ptr<C>& pc){ _pc = pc; } private: std::shared_ptr<C> _pc; }; class C : public std::enable_shared_from_this<C> { public: ~C(){ cout << "~C" << endl; } void SetPB(std::shared_ptr<B>& pb){ _pb = pb; } private: std::shared_ptr<B> _pb; }; int main() { std::shared_ptr<C> pc = std::make_shared<C>(); std::shared_ptr<B> pb = std::make_shared<B>(); pc->SetPB(pb); pb->SetPC(pc); return 0; }
上面的代碼中,B和C均不能正確析構(gòu),正確的做法是,在B和C的釋放函數(shù),如Close中,將其包含的shared_ptr置空。這樣才能解開(kāi)引用鏈。
二. 自引用
還有個(gè)比較有意思的例子:
class C : public std::enable_shared_from_this < C > { public: ~C() { std::cout << "~C" << std::endl; } int32_t Decode(const char* data, size_t) { return 0; } void SetDecoder(std::function<int32_t(const char*, size_t)> decoder) { _decoder = decoder; } private: std::function<int32_t(const char*, size_t)> _decoder; }; int main() { { std::shared_ptr<C> pc = std::make_shared<C>(); auto decoder = std::bind(&C::Decode, pc, std::placeholders::_1, std::placeholders::_2); pc->SetDecoder(decoder); } // C不能正確析構(gòu) 因?yàn)榇嬖谧砸? return 0; }
上面的C類(lèi)包含了一個(gè)function,該function通過(guò)std::bind引用了一個(gè)std::shared_ptr,所以_decoder其實(shí)包含了一個(gè)對(duì)shared_ptr的引用。導(dǎo)致C自引用了自身,不能正確析構(gòu)。需要在C的Close之類(lèi)的執(zhí)行關(guān)閉函數(shù)中,將_decoder=nullptr,以解開(kāi)這種自引用。
三. 類(lèi)中傳遞
下面的例子中有個(gè)更為隱蔽的問(wèn)題:
class Session : public std::enable_shared_from_this < Session > { public: ~Session() { std::cout << "~C" << std::endl; } void Start() { // 進(jìn)行一些異步調(diào)用 // 如 _socket.async_connect(..., boost::bind(&Session::ConnectCompleted, this), boost::asio::placeholders::error, ...) } void ConnectCompleted(const boost::system::err_code& err) { if(err) return; // ... 進(jìn)行處理 // 如 _socket.async_read(..., boost::bind(&Session::ReadCompleted, this), boost::asio::placeholders::error, ...) } void Session::ReadComplete(const boost::system::error_code& err, size_t bytes_transferred) { if (err || bytes_transferred == 0) { DisConnect(); return; } // 處理數(shù)據(jù) 繼續(xù)讀 // ProcessData(); // _socket.async_read(...) } private: std::function<int32_t(const char*, size_t)> _decoder; }; int main() { { std::shared_ptr<Session> pc = std::make_shared<Session>(); pc->Start(); } return 0; }
上面Session,在調(diào)用Start時(shí),調(diào)用了異步函數(shù),并回調(diào)自身,如果在回調(diào)函數(shù)的 boost::bind 中 傳入的是shared_from_this(),那么并無(wú)問(wèn)題,shared_ptr將被一直傳遞下去,在網(wǎng)絡(luò)處理正常時(shí),Session將正常運(yùn)行,即使main函數(shù)中已經(jīng)沒(méi)有它的引用,但是它靠boost::bind”活了下來(lái)”,boost::bind會(huì)保存?zhèn)鹘o它的shared_ptr,在調(diào)用函數(shù)時(shí)傳入。當(dāng)網(wǎng)絡(luò)遇到錯(cuò)誤時(shí),函數(shù)直接返回。此時(shí)不再有新的bind為其”續(xù)命”。Session將被析構(gòu)。
而真正的問(wèn)題在于,如果在整個(gè)bind鏈中,直接傳遞了this指針而不是shared_from_this(),那么實(shí)際上當(dāng)函數(shù)執(zhí)行完成后,Session即會(huì)析構(gòu),包括其內(nèi)部的資源(如 _socket)也會(huì)被釋放。那么當(dāng)boost底層去執(zhí)行網(wǎng)絡(luò)IO時(shí),自然會(huì)遇到錯(cuò)誤,并且仍然會(huì)”正?!被卣{(diào)到對(duì)應(yīng)函數(shù),如ReadCompleted,然后在err中告訴你:”由本地系統(tǒng)終止網(wǎng)絡(luò)連接”(或:”An attempt to abort the evaluation failed. The process is now in an indeterminate state.” )。讓人誤以為是網(wǎng)絡(luò)問(wèn)題,很難調(diào)試。而事實(shí)上此時(shí)整個(gè)對(duì)象都已經(jīng)被釋放掉了。
注:由于C++對(duì)象模型實(shí)現(xiàn)所致,成員函數(shù)和普通函數(shù)的主要區(qū)別如下:
- 成員函數(shù)帶隱式this參數(shù)
- 成員函數(shù)具有訪(fǎng)問(wèn)作用域,并且函數(shù)內(nèi)會(huì)對(duì)非靜態(tài)成員變量訪(fǎng)問(wèn)做一些轉(zhuǎn)換,如 _member_data 轉(zhuǎn)換成 this->_member_data;
也就是說(shuō),成員函數(shù)并不屬于對(duì)象,非靜態(tài)數(shù)據(jù)成員才屬于對(duì)象。
因此如下調(diào)用在編譯期是合法的:
((A*)nullptr)->Func();
而如果成員函數(shù)A::Func()沒(méi)有訪(fǎng)問(wèn)A的非靜態(tài)成員變量,這段代碼甚至能正確運(yùn)行,如:
class Test { public: void Say() { std::cout << "Say Test" << std::endl; } void Set(int data) { _data = data; } private: int _data; }; int main() { // 運(yùn)行成功 ((Test*)nullptr)->Say(); // 運(yùn)行會(huì)崩掉,嘗試訪(fǎng)問(wèn)空指針?biāo)竷?nèi)存(_data) ((Test*)nullptr)->Set(1); return 0; }
正因?yàn)檫@種特性,有時(shí)候在成員函數(shù)中糾結(jié)半天,也不會(huì)注意到這個(gè)對(duì)象已經(jīng)”不正常了”,被釋放掉了。
四. shared_ptr 使用總結(jié)
盡量不要環(huán)引用或自引用,可通過(guò)weak_ptr來(lái)避免環(huán)引用:owner持有child的shared_ptr child持有owner的weak_ptr
如果存在環(huán)引用或自引用,記得在釋放時(shí)解開(kāi)這個(gè)引用鏈
對(duì)于通過(guò)智能指針管理的類(lèi),在類(lèi)中通過(guò)shared_from_this()而不是this來(lái)傳遞本身
在類(lèi)釋放時(shí),盡量手動(dòng)置空其所有的shared_ptr成員,包括function
上一篇:C語(yǔ)言求解最長(zhǎng)公共子字符串問(wèn)題及相關(guān)的算法分析
欄 目:C語(yǔ)言
下一篇:C語(yǔ)言求Fibonacci斐波那契數(shù)列通項(xiàng)問(wèn)題的解法總結(jié)
本文標(biāo)題:詳解C++中shared_ptr的使用教程
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2251.html
您可能感興趣的文章
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)數(shù)怎么表達(dá)
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎么打出三角函數(shù)的值
- 01-10求子數(shù)組最大和的解決方法詳解
- 01-10深入二叉樹(shù)兩個(gè)結(jié)點(diǎn)的最低共同父結(jié)點(diǎn)的詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)- 解析最少換車(chē)次數(shù)的問(wèn)題詳解
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設(shè)計(jì)-用棧實(shí)現(xiàn)表達(dá)式求值的方法詳解
- 01-10HDOJ 1443 約瑟夫環(huán)的最新應(yīng)用分析詳解
- 01-10深入理解C++中常見(jiàn)的關(guān)鍵字含義


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹(shù)的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問(wèn)題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫(xiě)分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫(xiě)函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒(méi)有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-11Mac OSX 打開(kāi)原生自帶讀寫(xiě)NTFS功能(圖文
- 01-10SublimeText編譯C開(kāi)發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實(shí)例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10delphi制作wav文件的方法
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載