日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

shared_ptr简介以及常见问题

發(fā)布時間:2023/11/30 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 shared_ptr简介以及常见问题 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

http://blog.csdn.net/stelalala/article/details/19993425

本文中的shared_ptr以vs2010中的std::tr1::shared_ptr作為研究對象。可能和boost中的有些許差異,特此說明。

基本功能

shared_ptr提供了一個管理內(nèi)存的簡單有效的方法。shared_ptr能在以下方面給開發(fā)提供便利:

1、 使用shared_ptr能有效的解決忘記釋放內(nèi)存帶來的內(nèi)存泄漏問題。同時通過自定義刪除器功能還能廣泛的用于任何需要”釋放”的資源管理。

2、 利用weak_ptr和shared_ptr搭配使用能解決一部分由于重復(fù)釋放導(dǎo)致的野指針問題。

基本構(gòu)造方式

不談拷貝構(gòu)造的話,shared_ptr的基本構(gòu)造方式有4種:

1、 無參數(shù)構(gòu)造。

2、 傳入一個指針構(gòu)造一個shared_ptr。例如shared_ptr<Foo> f(new Foo)。

3、 傳入一個指針和一個刪除器構(gòu)造一個shared_ptr。例子見后文。

4、 傳入一個指針、一個刪除器以及一個allocator構(gòu)造一個shared_ptr。

當(dāng)然還有一些其他的,例如從auto_ptr從weak_ptr從null_ptr構(gòu)造。

另外,類似于void*,shared_ptr<void>可以容納任何類型的指針。

其他常用方法

l use_count()方法。獲取到當(dāng)前智能指針的引用計數(shù)。0表示沒有任何地方引用。

l get()方法。獲取到raw指針。

l reset()方法。重新設(shè)置智能指針指向的對象,引用計數(shù)重新設(shè)置為1。reset方法的函數(shù)原型有4種,基本上和前文提到的4種構(gòu)造函數(shù)一一對應(yīng)。

l swap()方法。交換兩個智能指針的內(nèi)容。

l operater =()方法。該方法會涉及到引用計數(shù)的增加。

關(guān)于自定義刪除器

智能指針能夠自定義刪除器是一個很重要的功能,該功能使得能夠跨dll傳遞shared_ptr變?yōu)榭赡?#xff08;當(dāng)然前提是多個dll使用的shared_ptr實現(xiàn)要一樣)。尤其是當(dāng)c++11的Lambda表達式出現(xiàn)后這個功能用起來更加方便。

先來看自定義刪除器的構(gòu)造方法:

template<class _Ux,

class _Dx>

shared_ptr(_Ux *_Px, _Dx _Dt)

{ // construct with _Px, deleter

_Resetp(_Px, _Dt);

}

其中構(gòu)造函數(shù)的第二個參數(shù)就是刪除器。這里要求刪除器:

1、 是”可調(diào)用”的即可,例如function object、函數(shù)指針、Lambda表達式、bind/functor等等均可。

2、 返回值是void,參數(shù)是Ux*

3、 從形參看出,刪除器以傳值的方式傳入,所以要求刪除器要是可拷貝的,否則會編譯出錯。

4、 刪除器不要拋出異常。

例如:

shared_ptr<Foo> shot1(new Foo(1),[&](Foo* p){p->Release();});

make_shared有何用處

boost或者stl都提供了make_shared這個函數(shù)。用來方便的創(chuàng)建shared_ptr。

make_shared的好處有兩點:

1、 既然用了shared_ptr不用手動delete指針,那么最好也不要在代碼中出現(xiàn)new。make_shared正是在函數(shù)內(nèi)封裝了new的操作。

2、 從shared_ptr的數(shù)據(jù)接口了解到,在構(gòu)造shared_ptr的時候,會new出一個對象保存指針的相關(guān)信息。所以一般來說,shared_ptr<Foo> x(new Foo); 需要為Foo 和ref_count 各分配一次內(nèi)存。如果使用make_shared來創(chuàng)建的話,make_shared內(nèi)部會盡量將兩次內(nèi)存分配在連續(xù)的位置(這個得看用的什么heap管理)。這里理論上能夠更快一些。

說下缺點:

1、 make_shared只能針對new出來的,對于使用工廠創(chuàng)建出來的對象無能為力。

2、 需要定制刪除器時,make_shared無能為力。

3、 make_shared目前只支持10個參數(shù)

另外,make_shared代碼很有意思,為了方便的定義10個參數(shù),宏定義用得鬼斧神工。

如何進行類型cast

如果只能指針聲明為基類的指針,指向的實際類型是子類的話,shared_ptr會自動完成。其他的轉(zhuǎn)型一眼就能看明白,無需多言:

tr1::const_pointer_cast

tr1::dynamic_pointer_cast

tr1::static_pointer_cast

使用shared_ptr可能會遇到的問題

生命周期的問題

使用shared_ptr的目的就是管理對象的生命周期。在使用了shared_ptr以后有幾個事情會變得和以往不太一樣。

首先,用了shared_ptr就表明對象是使用引用計數(shù)來管理,那么該對象什么時候真正被從內(nèi)存中釋放掉就不是很明顯了。比如說,可能你的代碼中持有了一份shared_ptr的拷貝,就會導(dǎo)致某個對象一直存留下來。

shared_ptr多次引用同一數(shù)據(jù)

發(fā)生這樣的事情后,最好的下場是:后釋放的shared_ptr在析構(gòu)的時候吐核。

在實際編碼中要注意。不要把一個raw指針交給多個shared_ptr管理。發(fā)生這樣的事情很可能是在遺留代碼上使用新特性導(dǎo)致的。

this指針的問題

例如這樣的例子:

class Foo

{

public:

Foo* GetThis()

{

return this;

}

}

要把這樣的代碼改為返回shared_ptr<Foo>,不那么好改。假如直接這樣修改會有嚴重的問題:

shared_ptr<Foo> GetThis()

{

return shared_ptr<Foo>(this);

}

因為shared_ptr<Foo>被使用完后就析構(gòu)了,引用計數(shù)減到0以后就會把this delete掉。照成野指針。

為了解決這個問題,標(biāo)準(zhǔn)庫提供了一個方法:讓類派生自一個模板類:enable_shared_from_this<T>。然后調(diào)用shared_from_this()函數(shù)即可。

class Foo: public enable_shared_from_this<Foo>

{

public:

shared_ptr<Foo> GetThis()

{

return shared_from_this();;

}

}

這個方法看上去不那么美觀,但是確實解決了一些問題。也帶來了另一些問題:shared_from_this()這個函數(shù)不能夠在構(gòu)造函數(shù)中調(diào)用。具體原理下一篇文章剖析shared_ptr實現(xiàn)原理時再講吧。

多線程的問題

shared_ptr的線程安全的定義在boost的文檔中有明確的說明:

l 一個shared_ptr對象可以被多個線程同時read

l 兩個shared_ptr對象,指向同一個raw指針,兩個個線程分別write這兩個shared_ptr對象,是安全的。包括析構(gòu)。

l 多個線程如果要對同一個shared_ptr對象讀寫,是線程不安全的

也就是說,唯一需要注意的就是:多個線程中對同一個shared_ptr對象讀寫時需要加鎖。但是即使是加鎖也有技巧。比較好的方式是:

thread.lock();

shared_ptr tmpPtr=globalSharedPtr; // globalSharedPtr是多個線程讀寫的那個

thread.unlock();

后面的操作均針對tmpPtr進行

環(huán)形引用的問題

環(huán)形引用是指這樣的情況:

Class A的一個實例中持有一個shared_ptr<B>,Class B的一個實例中持有shared_ptr<A>。考慮以下代碼:

class CParent

{

public:

shared_ptr< CChild > children;

};

class CChild

{

public:

shared_ptr< CParent > parent;

};

int main()

{

{

shared_ptr< CParent > pA(new CParent);

shared_ptr< CChild > pB(new CChild);

pA-> children =pB;

pB-> parent =pA;

}

//到這里pA和pB都未能被釋放掉

}

要解決環(huán)形引用,沒有特別好的辦法。在分析代碼以后,知道了在某個地方可能有環(huán)形引用,那么可以使用weak_ptr來替代shared_ptr。

weak_ptr

weak_ptr本身不具有指針的行為,例如你不能對一個weak_ptr來進行*或者->操作。它通常用來和shared_ptr配合使用。

weak_ptr作為一個”shared_ptr的觀察者”能夠獲知shared_ptr的引用計數(shù),還可以獲知一個shared_ptr是否已經(jīng)被析構(gòu)了。單沖這一點來說,就一點不weak了。

構(gòu)造weak_ptr

有兩種方法可以構(gòu)造一個weak_ptr

1、 從shared_ptr構(gòu)造而來。這種情況不會增加shared_ptr的引用計數(shù)。當(dāng)然會增加另一個計數(shù),這個放到下一篇中講。

2、 從另一個weak_ptr拷貝。

也就是說weak_ptr不可能脫離shared_ptr而存在。

expired()

返回布爾,當(dāng)返回true的時候表示,weak_ptr關(guān)聯(lián)的shared_ptr已經(jīng)被析構(gòu)了。

int _tmain(int argc, _TCHAR* argv[])

{

shared_ptr<foo> fptr=shared_ptr<foo>(new foo(1,2));

weak_ptr<foo> wptr=fptr;

fptr.reset();

if(wptr.expired())

{

cout<<”wptr has expired”<<endl;

}

system(“pause”);

return 0;

}

lock()

從當(dāng)前的weak_ptr創(chuàng)建一個新的shared_ptr。如果此時expired()返回true時,創(chuàng)建的shared_ptr中將保存一個null_ptr。

use_count()

返回當(dāng)前關(guān)聯(lián)的shared_ptr的引用計數(shù)是多少。expired()返回true時,該函數(shù)返回0。

weak_ptr使用場景

weak_ptr的特性是:weak_ptr不會增加shared_ptr的引用計數(shù),所以weak_ptr通常用來解決shared_ptr無法解決的問題,例如環(huán)形引用。weak_ptr常見的使用場景有這么幾個:

1、 想管理某些資源,但是又不想增加引用計數(shù),那么就可以保存weak_ptr。

2、 當(dāng)知道了有環(huán)形引用后,可以使用weak_ptr。例如上面的例子可以改為這樣:

class CParent

{

public:

shared_ptr< CChild > children;

};

class CChild

{

public:

weak_ptr< CParent > parent;

};

int main()

{

{

shared_ptr< CParent > pA(new CParent);

shared_ptr< CChild > pB(new CChild);

pA-> children =pB;

pB-> parent =pA;

}

}

3、 某些情況下,需要知道某個shared_ptr是否已經(jīng)釋放了。

總結(jié)

1、 在遺留代碼上如果要引入shared_ptr要謹慎!shared_ptr帶來的不確定性可能要比帶來的便利性大的多。

2、 使用shared_ptr并不是意味著能偷懶。反而你更需要了解用shared_ptr管理的對象的生命周期應(yīng)該是什么樣子的,是不是有環(huán)形引用,是不是有線程安全問題,是不是會在某個地方意外的被某個東西hold住了。

3、 一個對象如何使用shared_ptr管理那么最好全部使用shared_ptr來管理,必要的時候可以使用weak_ptr。千萬不要raw ptr和智能指針混用

3、 不要以傳遞指針的形式傳遞shared_ptr。

4、 多線程讀寫同一個shared_ptr的時候,可以先加鎖拷貝一份出來,然后解鎖即可。

參考

1、 1、《Boost程序庫完全開發(fā)指南》

2、 當(dāng)析構(gòu)函數(shù)遇到多線程──C++ 中線程安全的對象回調(diào)

http://www.cnblogs.com/Solstice/archive/2010/02/10/dtor_meets_threads.html

3、 為什么多線程讀寫shared_ptr 要加鎖

http://www.cnblogs.com/Solstice/archive/2013/01/28/2879366.html

4、 vc stl


————————以上文章轉(zhuǎn)自:shared_ptr簡介以及常見問題


總結(jié)

以上是生活随笔為你收集整理的shared_ptr简介以及常见问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。