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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

shared_ptr智能指针源码剖析

發布時間:2024/2/28 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 shared_ptr智能指针源码剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前幾天有個人問了我一個問題: 如何將一個智能指針作為函數的返回值傳遞出來。當時這個問題一下子把我問倒了,后來經人提醒有一個叫shared_ptr的智能指針可以解決這個問題。

將shared_ptr作為函數返回值的代碼是這樣的:

#include <tr1/memory> #include <stdio.h>using std::tr1::shared_ptr;shared_ptr<int> ReturnSharedPtr() {shared_ptr<int> p(new int(1000));return p; }int main() {shared_ptr<int> p1 = ReturnSharedPtr();printf("%d\n", *p1);return 0; }


在g++4.3版本以上編譯通過。shared_ptr頭文件的位置有點古怪,在我的DEBIAN(squeeze)機器上的這個地方:/usr/include/c++/4.4/tr1/shared_ptr.h,所以使用時要

#include <tr1/memory> 而且shared_ptr被包裝在std::tr1這個名字空間內。所以使用的時候和一般的stl模板的方式不大一樣, 要using std::tr1這個namespace。也可以用一個編譯器選項去掉這種古怪的定義方法, 具體是那個選項我查不到了。請了解的人幫忙指正一下。


shared_ptr的實現

看了一下stl的源碼,shared_ptr的實現是這樣的:? shared_ptr模板類有一個__shared_count類型的成員_M_refcount來處理引用計數的問題。__shared_count也是一個模板類,它的內部有一個指向Sp_counted_base_impl類型的指針_M_pi。所有引用同一個對象的shared_ptr都共用一個_M_pi指針。

當一個shared_ptr拷貝復制時, _M_pi指針調用_M_add_ref_copy()函數將引用計數+1。 當shared_ptr析構時,_M_pi指針調用_M_release()函數將引用計數-1。 _M_release()函數中會判斷引用計數是否為0. 如果引用計數為0, 則將shared_ptr引用的對象內存釋放掉。


__shared_count(const __shared_count& __r) : _M_pi(__r._M_pi) // nothrow{ if (_M_pi != 0)_M_pi->_M_add_ref_copy();COSTA_DEBUG_REFCOUNT;}

這是__shared_count拷貝復制時的代碼。首先將參數__r的_M_pi指針賦值給自己, 然后判斷指針是否為NULL, 如果不為null 則增加引用計數。COSTA_DEBUG_REFCOUNT和COSTA_DEBUG_SHAREDPTR是我為了打印引用計數的調試代碼,會打印文件行號和當前引用計數的值。


#define COSTA_DEBUG_REFCOUNT fprintf(stdout,"%s:%d costaxu debug refcount: %d\n", __FILE__,__LINE__,_M_pi->_M_get_use_count());#define COSTA_DEBUG_SHAREDPTR fprintf(stdout,"%s:%d costaxu debug \n", __FILE__,__LINE__);



__shared_count&operator=(const __shared_count& __r) // nothrow{ _Sp_counted_base<_Lp>* __tmp = __r._M_pi;if (__tmp != _M_pi){ if (__tmp != 0)__tmp->_M_add_ref_copy();if (_M_pi != 0)_M_pi->_M_release();_M_pi = __tmp;}COSTA_DEBUG_REFCOUNT;return *this;} 這是__share_count重載賦值操作符的代碼。 首先,判斷等號左右兩邊的__share_count是否引用同一個對象。如果引用同一個對象(__tmp==_M_pi),那么引用計數不變,什么都不用做。如果不是的話,就把等號左邊的share_ptr的引用計數-1,將等號右邊的引用計數+1 。例如: 有兩個shared_ptr p1和p2, 運行p1= p2 。 假如p1和p2是引用同一個對象的,那么引用計數不變。 如果p1和p2是指向不同對象的,那么p1所指向對象的引用計數-1, p2指向對象的引用計數+1。




~__shared_count() // nothrow{if (_M_pi != 0)_M_pi->_M_release();COSTA_DEBUG_REFCOUNT;} 上面是__share_count的析構函數, 其實析構函數只是調用了_M_pi的_M_release這個成員函數。_M_release這個函數,除了會將引用計數-1之外,還會判斷是否引用計數為0, 如果為0就調用_M_dispose()函數。 _M_dispose函數會將share_ptr引用的對象釋放內存。




virtual void_M_dispose() // nothrow{COSTA_DEBUG_SHAREDPTR;_M_del(_M_ptr);} _M_del是在構造_M_pi時候就初始化好的內存回收函數, _M_ptr就是shared_ptr引用的對象指針。


下面是我寫的一段簡單的測試代碼:


#include <stdio.h> #include <tr1/memory>using std::tr1::shared_ptr;shared_ptr<int> ReturnSharedPtr() {shared_ptr<int> p(new int(1000));shared_ptr<int> p2(p);shared_ptr<int> p3=p;shared_ptr<int> p4; p4=p2;return p; }int main() {shared_ptr<int> p1 = ReturnSharedPtr();printf("%d\n", *p1);return 0; }

下面是運行結果:

shared_ptr.h 169行是__shared_count拷貝構造時增加引用計數,184行是__shared_count賦值操作,161行是__share_count的析構時減少引用計數, 79行是釋放引用對象的內存。



/usr/include/c++/4.4/tr1/shared_ptr.h:169 costaxu debug refcount: 2 /usr/include/c++/4.4/tr1/shared_ptr.h:169 costaxu debug refcount: 3 /usr/include/c++/4.4/tr1/shared_ptr.h:184 costaxu debug refcount: 4 /usr/include/c++/4.4/tr1/shared_ptr.h:161 costaxu debug refcount: 3 /usr/include/c++/4.4/tr1/shared_ptr.h:161 costaxu debug refcount: 2 /usr/include/c++/4.4/tr1/shared_ptr.h:161 costaxu debug refcount: 1 1000 /usr/include/c++/4.4/tr1/shared_ptr.h:79 costaxu debug /usr/include/c++/4.4/tr1/shared_ptr.h:161 costaxu debug refcount: 0


shared_ptr線程安全性問題

關于shared_ptr的線程安全性。查了一些網上的資料,有的說是安全的,有的說不安全。引用CSDN上一篇比較老的帖子, 它是這樣說的:

Boost?文檔對于?shared_ptr?的線程安全有一段專門的記述,內容如下:
shared_ptr?objects?offer?the?same?level?of?thread?safety?as?built-in?types.?A?shared_ptr?instance?can?be?"read"?(accessed?using?only?const?operations)?simultaneously?by?multiple?threads.?Different?shared_ptr?instances?can?be?"written?to"?(accessed?using?mutable?operations?such?as?operator=?or?reset)?simultaneosly?by?multiple?threads?(even?when?these?instances?are?copies,?and?share?the?same?reference?count?underneath.)
Any?other?simultaneous?accesses?result?in?undefined?behavior.
翻譯為中文如下:
shared_ptr?對象提供與內建類型一樣的線程安全級別。一個?shared_ptr?實例可以同時被多個線程“讀”(僅使用不變操作進行訪問)。 不同的?shared_ptr?實例可以同時被多個線程“寫入”(使用類似?operator=?或?reset?這樣的可變操作進行訪問)(即使這些實 例是拷貝,而且共享下層的引用計數)。
任何其它的同時訪問的結果會導致未定義行為。”

這幾句話比較繁瑣,我總結一下它的意思:

1 同一個shared_ptr被多個線程“讀”是安全的。

2 同一個shared_ptr被多個線程“寫”是不安全的。

3 共享引用計數的不同的shared_ptr被多個線程”寫“ 是安全的。

如何印證上面的觀點呢?

其實第一點我覺得比較多余。因為在多個線程中讀同一個對象,在正常情況下不會有什么問題。

所以問題就是:如何寫程序證明同一個shared_ptr被多個線程"寫"是不安全的?

我的思路是,在多個線程中同時對一個shared_ptr循環執行兩遍swap。 shared_ptr的swap函數的作用就是和另外一個shared_ptr交換引用對象和引用計數,是寫操作。執行兩遍swap之后, shared_ptr引用的對象的值應該不變。

程序如下:


#include <stdio.h> #include <tr1/memory> #include <pthread.h>using std::tr1::shared_ptr;shared_ptr<int> gp(new int(2000));shared_ptr<int> CostaSwapSharedPtr1(shared_ptr<int> & p) {shared_ptr<int> p1(p);shared_ptr<int> p2(new int(1000));p1.swap(p2);p2.swap(p1);return p1; }shared_ptr<int> CostaSwapSharedPtr2(shared_ptr<int> & p) {shared_ptr<int> p2(new int(1000));p.swap(p2);p2.swap(p);return p; }void* thread_start(void * arg) {int i =0;for(;i<100000;i++){shared_ptr<int> p= CostaSwapSharedPtr2(gp);if(*p!=2000){printf("Thread error. *gp=%d \n", *gp);break;}}printf("Thread quit \n");return 0; }int main() {pthread_t thread;int thread_num = 10, i=0;pthread_t* threads = new pthread_t[thread_num];for(;i<thread_num;i++)pthread_create(&threads[i], 0 , thread_start , &i);for(i=0;i<thread_num;i++)pthread_join(threads[i],0);delete[] threads;return 0; }



這個程序中我啟了10個線程。每個線程調用10萬次 CostaSwapSharedPtr2函數。 在CostaSwapSharePtr2函數中,對同一個share_ptr全局變量gp進行兩次swap(寫操作), 在函數返回之后檢查gp的值是否被修改。如果gp值被修改,則證明多線程對同一個share_ptr執行寫操作是不安全的。

程序運行的結果如下:

Thread error. *gp=1000 Thread error. *gp=1000 Thread quit Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread error. *gp=1000 Thread quit Thread quit 10個線程有9個出錯。證明多線程對同一個share_ptr執行寫操作是不安全的。我們在程序中,如果不運行CostaSwapSharedPtr2, 改成運行CostaSwapSharedPtr1呢?? CostaSwapSharedPtr1和CostaSwapSharedPtr2的區別在于, 它不是直接對全局變量gp進行寫操作,而是將gp拷貝出來一份再進行寫操作。運行的結果如下: costa@pepsi:~/test/cpp/shared_ptr$ ./b Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit Thread quit

跑了很多次都沒有出錯。說明共享引用計數的不同的shared_ptr執行swap是線程安全的。BOOST文檔是可信的。

補充一個問題:?為什么shared_ptr可以作為STL標準容器的元素,而auto_ptr不可以

這篇文章小結一下:

1 shared_ptr是一個非常實用的智能指針。

2 shared_ptr的實現機制是在拷貝構造時使用同一份引用計數。

3 對同一個shared_ptr的寫操作不是線程安全的。 對使用同一份引用計數的不同shared_ptr是線程安全的。


總結

以上是生活随笔為你收集整理的shared_ptr智能指针源码剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。