一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

shared_ptr陷阱

 haodafeng_org 2014-07-03
條款1:不要把一個原生指針給多個shared_ptr管理
int* ptr = new int;
shared_ptr<int> p1(ptr);
shared_ptr<int> p2(ptr); //logic error
ptr對象被刪除了2次
這種問題比喻成“二龍治水”,在原生指針中也同樣可能發(fā)生。

條款2:不要把this指針給shared_ptr
class Test{
public:
    void Do(){  m_sp =  shared_ptr<Test>(this);  }
private:
    shared_ptr<Test> m_member_sp;
};

Test* t = new Test;
shared_ptr<Test> local_sp(t);
p->Do();

發(fā)生什么事呢,t對象被刪除了2次!
t對象給了local_sp管理,然后在m_sp =  shared_ptr<Test>(this)這句里又請了一尊神來管理t。
這就發(fā)生了條款1里“二龍治水”錯誤。

條款3:shared_ptr作為被保護(hù)的對象的成員時,小心因循環(huán)引用造成無法釋放資源。

對象需要相互協(xié)作,對象A需要知道對象B的地址,這樣才能給對象B發(fā)消息(或調(diào)用其方法)。
設(shè)計模式中有大量例子,一個對象中有其他對象的指針?,F(xiàn)在把原生指針替換為shared_ptr.

假設(shè)a對象中含有一個shared_ptr<B>指向b對象;假設(shè)b對象中含有一個shared_ptr<A>指向a對象
并且a,b對象都是堆中分配的。很輕易就能與他們失去最后聯(lián)系。
考慮某個shared_ptr<A> local_a;是我們能最后一個看到a對象的共享智能指針,其use_count==2,
因為對象b中持有a的指針。所以當(dāng)local_a說再見時,local_a只是把a(bǔ)對象的use_count改成1。
同理b對象。然后我們再也看不到a,b的影子了,他們就靜靜的躺在堆里,成為斷線的風(fēng)箏。

解決方案是:Use weak_ptr to "break cycles."(boost文檔里寫的)或者顯示的清理

條款4:不要在函數(shù)實參里創(chuàng)建shared_ptr

function ( shared_ptr<int>(new int), g( ) );  //有缺陷
可能的過程是先new int,然后調(diào)g( ),g( )發(fā)生異常,shared_ptr<int>沒有創(chuàng)建,int內(nèi)存泄露

shared_ptr<int> p(new int());
f(p, g());  //Boost推薦寫法

條款5:對象內(nèi)部生成shared_ptr

前面說過,不能把this指針直接扔給shared_ptr. 但是沒有禁止在對象內(nèi)部生成自己的shared_ptr

//這是Boost的例子改的。
class Y: public boost::enable_shared_from_this<Y>
{
    boost::shared_ptr<Y> GetSelf()
    {
        return shared_from_this();
    }
};

原理是這樣的。普通的(沒有繼承enable_shared_from_this)類T的shared_ptr<T> p(new T).
p作為棧對象占8個字節(jié),為了記錄(new T)對象的引用計數(shù),p會在堆上分配16個字節(jié)以保存
引用計數(shù)等“智能信息”。share_ptr沒有“嵌入(intrusive)”到T對象,或者說T對象對share_ptr毫不知

情。Y對象則不同,Y對象已經(jīng)被“嵌入”了一些share_ptr相關(guān)的信息,目的是為了找到“全局性”的
那16字節(jié)的本對象的“智能信息”。

原理說完了,就是陷阱
Y y;
boost::shared_ptr<Y> p=  y.GetSelf(); //無知的代碼,y根本就不是new出來的

Y* y = new Y;
boost::shared_ptr<Y> p=  y->GetSelf(); //似是而非,仍舊程序崩盤。
Boost文檔說,在調(diào)用shared_from_this()之前,必須存在一個正常途徑創(chuàng)建的shared_ptr

boost::shared_ptr<Y> spy(new Y)
boost::shared_ptr<Y> p =  spy->GetSelf(); //OK

條款6 :處理不是new的對象要小心。

int* pi = (int*)malloc(4)
shared_ptr<int> sp( pi ) ; //delete馬嘴不對malloc驢頭。

條款7:多線程對引用計數(shù)的影響。

如果是輕量級的鎖,比如InterLockIncrement等,對程序影響不大
如果是重量級的鎖,就要考慮因為share_ptr維護(hù)引用計數(shù)而造成的上下文切換開銷。
1.33版本以后的shared_ptr對引用計數(shù)的操作使用的是Lock-Free(類似InterLockIncrement函數(shù)族)
的操作,應(yīng)該效率不錯,而且能保證線程安全(庫必須保證其安全,程序員都沒有干預(yù)這些隱藏事物的機(jī)會)。
Boost文檔說read,write同時對shared_ptr操作時,行為不確定。這是因為shared_ptr本身有兩個成員px,pi。
多線程同時對px讀寫是要出問題的。與一個int的全局變量多線程讀寫會出問題的原因一樣。

條款8:對象數(shù)組用shared_array

int* pint = new int[100];
shared_array<int> p (pint );

既然shared_ptr對應(yīng)著delete;顯然需要一個delete[]對應(yīng)物shared_array

條款9:學(xué)會用刪除器

struct Test_Deleter
{   
    void  operator ()( Test* p){   ::free(p);   }
};
Test* t = (Test*)malloc(sizeof(Test));
new (t) Test;

shared_ptr<Test> sp( t ,  Test_Deleter() ); //刪除器可以改變share_ptr銷毀對象行為

有了刪除器,shared_array無用武之地了。
template<class T>
struct Array_Deleter
{   
    void  operator ()( T*){   delete[] p;   }
};
int* pint = new int[100];
shared_ptr<int> p (pint, Array_Deleter<int>() );

條款10:學(xué)會用分配器

存放引用計數(shù)的地方是堆內(nèi)存,需要16-20字節(jié)的開銷。
如果大量使用shared_ptr會造成大量內(nèi)存碎片。
shared_ptr構(gòu)造函數(shù)的第3個參數(shù)是分配器,可以解決這個問題。

shared_ptr<Test> p( (new Test), Test_Deleter(), Mallocator<Test>() );
注意刪除器Test_Deleter是針對Test類的。分配器是針對shared_ptr內(nèi)部數(shù)據(jù)的。

Mallocator<Test>()是個臨時對象(無狀態(tài)的),符合STL分配器規(guī)約。

template <typename T> 
class Mallocator { 
    //略。。。。。。
    T * allocate(const size_t n) const {
        return singleton_pool<T,sizeof(T)>::malloc();
    }
    //略。。。。。。

Mallocator傳入Test,實際分配的類型確是
class boost::detail::sp_counted_impl_pda<class Test *,
                                         struct Test_Deleter,
                                         class Mallocator<class Test> >
這是用typeid(T).name()打印出來的??赡芎蛂ebind相關(guān)。

條款11 weak_ptr在使用前需要檢查合法性。
weak_ptr<K> wp;
{
shared_ptr<K>  sp(new K);  //sp.use_count()==1
wp = sp; //wp不會改變引用計數(shù),所以sp.use_count()==1
shared_ptr<K> sp_ok = wp.lock(); //wp沒有重載->操作符。只能這樣取所指向的對象
}
shared_ptr<K> sp_null = wp.lock(); //sp_null .use_count()==0;
因為上述代碼中sp和sp_ok離開了作用域,其容納的K對象已經(jīng)被釋放了。
得到了一個容納NULL指針的sp_null對象。在使用wp前需要調(diào)用wp.expired()函數(shù)判斷一下。
因為wp還仍舊存在,雖然引用計數(shù)等于0,仍有某處“全局”性的存儲塊保存著這個計數(shù)信息。
直到最后一個weak_ptr對象被析構(gòu),這塊“堆”存儲塊才能被回收。否則weak_ptr無法直到自己
所容納的那個指針資源的當(dāng)前狀態(tài)。

條款12 不要new shared_ptr<T>

本來shared_ptr就是為了管理指針資源的,不要又引入一個需要管理的指針資源shared_ptr<T>*

條款13  盡量不要get

class B{...};
class D : public B{ ...};  //繼承層次關(guān)系

shared_ptr<B> sp (new D);    //通過隱式轉(zhuǎn)換,儲存D的指針。
B* b = sp.get();             //shared_ptr辛辛苦苦隱藏的原生指針就這么被刨出來了。
D* d = dynamic_cast<D*>(b);  //這是使用get的正當(dāng)理由嗎?

正確的做法
shared_ptr<B> spb (new D)  ;
shared_ptr<D> spd = shared_dynamic_cast<D>(spb); //變成子類的指針
shared_ptr在竭盡全力表演的像一個原生指針,原生指針能干的事,它也基本上能干。

另一個同get相關(guān)的錯誤
shared_ptr<T> sp(new T);
shared_ptr<T> sp2( sp.get() ) ;//又一個“二龍治水”實例,指針會刪2次而錯誤。

條款14 不要memcpy shared_ptr

shared_ptr<B> sp1 (new B)  ;
shared_ptr<B> sp2;
memcpy(&sp2,&sp1,sizeof(shared_ptr<B>)); //sp2.use_count()==1
很顯然,不是通過正常途徑(拷貝構(gòu)造,賦值運(yùn)算),引用計數(shù)是不會正確增長的。

條款15 使用BOOST預(yù)定義的宏去改變shared_ptr行為。

shared_ptr行為由類似BOOST_SP_DISABLE_THREADS這樣的宏控制。需要去學(xué)習(xí)他們到底是干什么的。
大師Andrei Alexandrescu設(shè)計了一種基于模板策略設(shè)計模式的智能指針,通過幾個模板參數(shù)去定制化
智能指針的行為。Boost卻不以為然,官方解釋是:需要統(tǒng)一的接口,這樣利于大規(guī)模書寫。
smart_ptr<T,OwnershipPolicy,ConversionPolicy,CheckingPolicy,StoragePolicy> sp(new T);
上述接口缺點(diǎn)是外形復(fù)雜,看上去像個大花臉。優(yōu)點(diǎn)是客戶程序員可以輕易的定制行為。

條款17 構(gòu)造函數(shù)里調(diào)用shared_from_this拋例外

class Holder:public enable_shared_from_this<Holder>{
public:
    Holder() {
        shared_ptr<Holder> sp = shared_from_this();
        int x = sp.use_count();
    }
};
同前面條款5,不符合enable_shared_from_this使用前提。

總結(jié):
學(xué)習(xí)了一天就總結(jié)出10多條條款,長期研究一下恐怕就出現(xiàn)條款100了。為什么還要使用shared_ptr呢?
有很多開源庫用shared_ptr,而且shared_ptr具有“傳染性”(某網(wǎng)友語:像毒品沾上就甩不掉),
拋開它就會有更嚴(yán)重的多龍治水現(xiàn)象。shared_ptr作為原生指針的替代品,能解決一定的內(nèi)存泄露問題。
實際上初學(xué)原生指針時,每個人都遇到過野指針,刪兩次,忘記刪除等問題。學(xué)習(xí)shared_ptr也會遇到。
shared_ptr的確能改善上述問題,并不能完全解決問題。shared_ptr可能在將來占主流,它最可能號令江湖,
否則一大堆a(bǔ)uto_ptr,weak_ptr,原生指針,scoped_ptr共存就把人搞糊涂了。
 

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    大香蕉大香蕉手机在线视频| 国产精品一区二区香蕉视频| 午夜国产精品福利在线观看| 亚洲夫妻性生活免费视频| 亚洲欧美日本视频一区二区| 国产午夜精品在线免费看| 国产精品白丝一区二区| 国内欲色一区二区三区| 久久午夜福利精品日韩| 欧美午夜视频免费观看| av一区二区三区天堂| 日本东京热视频一区二区三区| 日本加勒比在线播放一区| 91在线播放在线播放观看| 日本理论片午夜在线观看| 国产精品伦一区二区三区在线| 国产内射一级二级三级| 丝袜人妻夜夜爽一区二区三区| 日韩av生活片一区二区三区| 国产不卡视频一区在线| 99久久人妻精品免费一区| 日韩精品在线观看一区| 99久久精品国产日本| 亚洲最新一区二区三区| 国产精品不卡免费视频| 亚洲一区二区久久观看| 国产一区二区三区四区免费| 亚洲精品偷拍一区二区三区| 亚洲精品中文字幕一二三| 亚洲综合一区二区三区在线| 91欧美日韩精品在线| 欧美尤物在线视频91| 欧美色婷婷综合狠狠爱| 日本少妇中文字幕不卡视频| 国产成人在线一区二区三区| 亚洲一区二区三区免费的视频| 国产欧美日韩在线一区二区| 亚洲综合一区二区三区在线| 99久久精品免费看国产高清| 小草少妇视频免费看视频| 二区久久久国产av色|