智能指針常見錯(cuò)誤用法
前言
智能指針的出現(xiàn)大大減輕了 C++ 程序員的心理負(fù)擔(dān)(最少對(duì)于我是這樣的),不用再時(shí)時(shí)刻刻擔(dān)心一個(gè) new 出來的指針是否被 delete 的問題了。雖然智能指針很強(qiáng)大,但是如果用不好,還是會(huì)導(dǎo)致各種各樣的問題。最近,在項(xiàng)目里看到了幾種智能指針的典型錯(cuò)誤用法。有的嚴(yán)重,有的輕,有的問題在研發(fā)階段并沒有立刻暴露出來,埋下了一顆定時(shí)炸彈。趁著這個(gè)機(jī)會(huì),總結(jié)一下幾種常見的錯(cuò)誤用法。希望對(duì)各位小伙伴兒有幫助。
說明:智能指針有很多種,標(biāo)準(zhǔn)庫(kù)提供了幾種,比如最常用的 shared_ptr 和 unique_ptr, 比較少用的 weak_ptr 以及被廢棄的 auto_ptr。項(xiàng)目代碼里還有一種基于引用計(jì)數(shù)的智能指針。本文只列舉幾種常見的錯(cuò)誤用法,不會(huì)深入到各種智能指針的實(shí)現(xiàn)。如果想深刻理解智能指針的用法,一定要看源碼。
錯(cuò)誤用法
在列舉各種錯(cuò)誤用法之前,想說明一點(diǎn):這些錯(cuò)誤都是實(shí)際遇到過的,并不是憑空想出來的。所以,即使有些示例代碼錯(cuò)的是那么明顯,也請(qǐng)不要輕視。
1. 同一指針交給兩個(gè)智能指針管理導(dǎo)致二次釋放
namespace?case1
{
????/*
????*?pRawData?is?already?managed?by?a?shared_ptr?(pData?in?function?#Entry()),
????*?in?function?#Use(),?another?shared_ptr?(pNewData)?manages?the?raw?pointer?too.
????*?now,?we?have?two?shared_ptr?manage?the?same?raw?pointer.?Oops,?double?free!
????*/
????static?void?Use(Common::CDerived*?pRawData)
????{
????????std::shared_ptr?pNewData(pRawData) ;
????????pNewData->DoSomething();
????}
????static?void?Entry()
????{
????????auto?pData?=?std::make_shared();
????????Use(pData.get());
????}
}
2. 錯(cuò)誤的動(dòng)態(tài)轉(zhuǎn)換導(dǎo)致的二次釋放
#pragma?once
#include?
#include?"common.h"
namespace?case2
{
????static?void?Use(std::shared_ptr?pData)
????{
????????pData->DoSomething();
????}
????static?std::shared_ptr?GetData()
???? {
????????return?std::make_shared();
????}
????static?void?Entry()
????{
????????auto?pTest?=?std::make_shared();
????????//?Oops,?double?free
????????std::shared_ptr?pData?=?GetData();
????????auto?pRawData?=?pData.get();
????????Common::CDerived*?pDerived?=?dynamic_cast(pRawData);
????????if?(pDerived)
????????{
????????????Use(std::shared_ptr(pDerived));
????????}
????????//?code?below?is?good
????????//Use(std::dynamic_pointer_cast(pData));
????}
}
3. 返回智能指針管理的原生指針
namespace?case3
{
????/*
????*?after?#ReturnRawPointer()?and?#GetRawPointer()?returned,?shared_ptr's?destructor?is?called,
????*?then?the?returned?raw?pointer?points?to?a?deleted?address.?bang!
????*/
????static?Common::CDerived*?ReturnRawPointer()
????{
????????auto?pData?=?std::make_shared();
????????return?pData.get();
????}
????static?bool?GetRawPointer(Common::CDerived*?&?pReturnedData)
????{
????????auto?pData?=?std::make_shared();
????????pReturnedData?=?pData.get();
????????return?(pReturnedData?!=?nullptr);
????}
????static?void?Entry()
????{
????????auto?pData?=?ReturnRawPointer();
????????pData->DoSomething();
????????pData?=?nullptr;
????????GetRawPointer(pData);
????????pData->DoSomething();
????}
}
4. 類中的成員變量指針交給外部智能指針管理
namespace?case4
{
????/*
????*?CTest::pData?is?managed?by?CTest,?but?when?call?#Use(),?it?is?used?to?construct?a?shared_pt,
????*?then?it?is?also?managed?by?a?shared_ptr.?double?free!
????*/
????class?CTest
????{
????public:
????????CTest()?{?pData?=?new?Common::CDerived();?}
????????~CTest()?{?delete?pData;?}
????????Common::CDerived*?pData;
????};
????/*?this?function?need?a?shared_ptr,?this?is?ok.*/
????static?void?Use(std::shared_ptr?pData)
????{
????????pData->DoSomething();
????}
????static?void?Entry()
????{
????????auto?pTest?=?std::make_shared();
????????Use(std::shared_ptr(pTest->pData));
????}
}
5. 棧變量的地址交給智能指針管理
namespace?case5
{
????/*
????*?data?is?on?stack,?after?managed?by?a?shared_ptr,?it?will?be?deleted.
????*?we?CAN?NOT?delete?an?address?on?stack!
????*/
????static?void?Use(std::shared_ptr?pData)
????{
????????pData->DoSomething();
????}
????static?void?Entry()
????{
????????//?well,?I?have?simplify?this?error?quite?much.
????????Common::CDerived?data;
????????auto?pData?=?std::shared_ptr(&data);
????????Use(pData);
????}
}
6. 循環(huán)引用導(dǎo)致的內(nèi)存泄漏
#pragma?once
#include?
#include?
#include?"common.h"
namespace?case6
{
????class?CPerson
????{
????public:
????????CPerson()?{?printf(__FUNCTION__?"\n");?}
????????virtual?~CPerson()?{?printf(__FUNCTION__?"\n");?}
????};
????class?CParent?:?public?CPerson
????{
????public:
????????CParent()?{?printf(__FUNCTION__?"\n");?}
????????virtual?~CParent()?{?printf(__FUNCTION__?"\n");?}
????????std::vector<std::shared_ptr>?children;
????};
????class?CChild?:?public?CPerson
????{
????public:
????????CChild()?{?printf(__FUNCTION__?"\n");?}
????????virtual?~CChild()?{?printf(__FUNCTION__?"\n");?}
????????std::shared_ptr?parent;
????};
????static?void?Entry()
????{
????????//?Oops,?no?one?will?be?died?then.
????????std::shared_ptr?parent?=?std::make_shared();
????????std::shared_ptr?child1?=?std::make_shared();
????????std::shared_ptr?child2?=?std::make_shared();
????????std::shared_ptr?child3?=?std::make_shared();
????????parent->children.push_back(child1);
????????parent->children.push_back(child2);
????????parent->children.push_back(child3);
????????child1->parent?=?parent;
????????child2->parent?=?parent;
????????child3->parent?=?parent;
????}
}
示例代碼
完整的示例代碼下載地址
CSDN:https://download.csdn.net/download/xiaoyanilw/13203123
百度云:https://pan.baidu.com/s/1dFUKevDXJZfja3HyO-jazg 提取碼: 8ra8
總結(jié)
以上示例代碼雖然有的看起來非常不可思議,這是我簡(jiǎn)化后的結(jié)果,在實(shí)際代碼中經(jīng)常以另外一種形式出現(xiàn),一不小心就容易中招。
