計數智能指標要點(shared_ptr)

來源:互聯網
上載者:User
引言: 與java等眾多支援GC的現代語言不同,C/C++將更多的記憶體控制權交給程式員,在保證效率的同時,也給了很多犯錯的機會。常見的記憶體泄露、重複釋放等等。智能指標大大減少了犯錯的機會,簡化代碼,提高可維護性。常用的智能指標有scope_ptr(c++ 11 unique_ptr),利用RAII特性,保證資源在範圍失效的時候被釋放,也保證異常拋出時棧復原能夠釋放資源;引用計數智能指標(shared_ptr),用於某個資源被多個owner共用,因此容易出現誤刪,甚至不知道該不該刪。shared_ptr會保證只有最後一個owner釋放資源一次。1.引用計數簡單來說,shared_ptr利用一個owner計數跟蹤它所管理的指標,以確保當沒有owner的時候,釋放指標。因此這個計數需要被共用,可勾勒出sharedptr成員如下:虛擬碼:class shared_ptr{   int*  m_ref; // 引用計數   T*    m_ptr;};如果m_ptr不為空白,m_ref應該初始化為1: m_ref = new int(1);shared_ptr的拷貝建構函式和賦值函數應該增加資源計數:++ *m_ref;解構函式遞減計數:if (m_ref && -- *m_ref == 0){   delete m_ref;   delete m_ptr;}2.引用計數的管理從上面可以看出,sharedptr不僅管理資源,還要管理計數,且資源僅支援預設的delete釋放。不是很方便。因此將引用計數封裝一下,當計數降0時自動釋放自己。虛擬碼:class CounterBase{   CounterBase() : m_ref(1) {}  virtual ~CounterBase()   {  }  void Destroy()  {     delete this;   }   void DecRef()  {    if (-- m_ref == 0)       Destroy();  }   // 其餘操作略:計數的增加   int  m_ref;};class shared_ptr{   CounterBase*  m_ref; // 引用計數,析構時候不用管它了   T*    m_ptr;};3.資源的釋放上面已經說過,資源釋放僅僅支援delete;拋開數組new,先考慮以下情形:FILE*  fp = fopen(xxx);shared_ptr<FILE>  file(fp);顯然不希望執行delete fp,而是 fclose(fp);因此需要第二個參數傳給sharedptr:deleter func object為了便於管理資源,將m_ptr移到CounterBase類,由於釋放資源的方式不確定,提供虛函數Dispose();class CounterBase{   void DecRef()   {     if (-- m_ref == 0)     { +      Dispose();       Destroy();     }  } +   virtual void Dispose(); // 釋放資源 +   T* m_ptr;};派生一個類,該類比基類多了一個deleter對象m_deleter.Dispose()預設實現是 delete  m_ptr;定製實現是m_deleter(m_ptr);對於上面的例子,我們可以傳入這樣的deleter:struct CloseFile{void operator()(File* fp){  if (fp)  fclose(fp);}};對於C++11你可以使用lamdba避免這個冗繁的定義。FILE*  fp = fopen(xxx);shared_ptr<FILE>  file(fp, CloseFile());這樣,fp就可以正常的被關閉了。4.多線程20年前的C++是只考慮單線程的,但現在早就不一樣了。為了保證計數的執行緒安全性,需要用到atomic系列函數封裝。比較簡單點,用相應API替換原始的++ --操作即可。boost文檔給了一些例子,這裡不分析了,如果熟悉實現原理,很容易理解為什麼某些操作不是安全執行緒的。分析一下官方文檔上給出的一些例子吧。Examples:shared_ptr<int> p(new int(42));// Ex 1// thread Ashared_ptr<int> p2(p);// thread Bshared_ptr<int> p3(p);這是安全的。因為是兩個線程對計數同時執行原子遞增函數,安全。// Ex 2// thread Ap.reset(new int(1912);// thread Bp2.reset;這是安全的。儘管兩個智能指標指向同一個資源,reset操作僅僅是讓智能指標內部的計數指標和資源指標指向新的資源,對於老的資源,只不過是同時執行了原子遞減函數,安全。// Ex 3// thread Ap = p3;// thread Bp3.reset();這是不安全的。考慮以下執行流:thread A                    thread B

                                對內部引用計數遞減,且剛好減為0;//1

對內部引用計數遞增;//2                                    釋放資源,刪除引用計數(假設沒有weak_ptr引用該資源)// 3。。。等著掛吧!// Ex 4// thread Ap3 = p2;// thread Bp2 goes out of scope;原理同上,一樣的是不安全行為;// Ex 5// thread Ap3.reset(new int(1));// thread Bp3.reset(new int(2));原理同上,一樣的是不安全行為;

5.循環參考考慮如下情形:class B; // 並不需要B的完整定義。因為shared_ptr僅擁有指標成員,且不執行delete之類的操作。class A{ shared_ptr<B>  m_pb;};class B{ shared_ptr<A>  m_pa;};shared_ptr<A>  pa(new A);shared_ptr<B>  pb(new B);pa->m_pb = pb;pb->m_pa = pa;你會發現,pa和pb由於互相指向對方,引用計數不會歸0導致資源不釋放。為此引入了一種稱作weak_ptr的東西,本質來講,它並不是智能指標,若訪問資源需要由它構造一個shared_ptr,可以認為它是對引用計數的一個計數(weak_count),不是資源的計數(share_count)。因此,只要將shared_ptr<A>  m_pa;改為weak_ptr<A>  m_pa;資源即可正常釋放,而此時很有可能引用計數對象還存在,只要有weak_ptr指向它;所以使用weak_ptr需要先調用lock()擷取shared_ptr,若不為空白才能訪問資源。6.典型錯誤struct A{  int a;};shared_ptr<A>  pa(new A);shared_ptr<int> pint(&pa->a);上面的代碼顯然是錯誤的;成員a屬於A對象,生命期由後者控制。為此shared_ptr提供了一個成為alias ctor的函數,正確用法如下:shared_ptr<int> pint(pa, &pa->a);pint依然與pa共用資源計數,但是,pint包裹的指標不同。這大概也是為什麼shared_ptr有一個T*,計數對象也有一個T*,貌似重複,其實不然。再引用書上的例子:int* p = new int(0);shared_ptr<int> sp1(p);shared_ptr<int> sp2(p);這是錯誤的,p將被釋放兩次;正確做法是:shared_ptr<int> sp2(sp1);另外要關注一下enable_shared_from_this的用法,弄懂了weak_ptr應該是很容易寫出實現的。shared_ptr代碼量小,功能又很實用,強烈建議不熟悉的自己手寫實現一遍,可以看boost例子,但是不要去看boost的代碼實現。實現完成後可以再去做個對比,有什麼差異、為什麼。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.