1. 程式人生 > >計數智慧指標要點(shared_ptr)

計數智慧指標要點(shared_ptr)

引言:  與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 A shared_ptr<int> p2(p); // thread B shared_ptr<int> p3(p); 這是安全的。因為是兩個執行緒對計數同時執行原子遞增函式,安全。 // Ex 2 // thread A p.reset(new int(1912); // thread B p2.reset; 這是安全的。 儘管兩個智慧指標指向同一個資源,reset操作僅僅是讓智慧指標內部的計數指標和資源指標指向新的資源,對於老的資源,只不過是同時執行了原子遞減函式,安全。 // Ex 3 // thread A p = p3; // thread B p3.reset(); 這是不安全的。考慮以下執行流: thread A                    thread B                                 對內部引用計數遞減,且剛好減為0;//1 對內部引用計數遞增;//2                                     釋放資源,刪除引用計數(假設沒有weak_ptr引用該資源)// 3 。。。等著掛吧! // Ex 4 // thread A p3 = p2; // thread B p2 goes out of scope; 原理同上,一樣的是不安全行為; // Ex 5 // thread A p3.reset(new int(1)); // thread B p3.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的程式碼實現。實現完成後可以再去做個對比,有什 麼差異、為什麼。

相關推薦

計數智慧指標要點(shared_ptr)

引言:  與java等眾多支援GC的現代語言不同,C/C++將更多的記憶體控制權交給程式設計師,在保證 效率的同時,也給了很多犯錯的機會。常見的記憶體洩露、重複釋放等等。智慧指標大大 減少了犯錯的機會,簡化程式碼,提高可維護性。常用的智慧指標有scope_ptr(c

【轉載】C++ 智慧指標shared_ptr/weak_ptr)原始碼分析

發現一篇對C++11智慧指標分析很透徹的文章,特轉載備忘! 以下轉載自:https://blog.csdn.net/ithiker/article/details/51532484?utm_source=blogxgwz1   C++11目前已經引入了unique_ptr, shared_pt

C++之智慧指標std::shared_ptr簡單使用和理解

1  智慧指標std::shared_ptr相關知識和如何使用 我們這裡先說下智慧指標std::shared_ptr,因為我看到我我們專案c++程式碼裡面用得很多,我不是不會,所以記錄學習下 先讓ubuntu終端支援c++11,如果自己的電腦還沒配置號,可以先看下我的這篇部落格

C++智慧指標shared_ptr,uniqe_ptr,weak_ptr

動態記憶體 在C++中,動態記憶體的管理是通過一對運算子來完成的:new和delete。 new:在動態記憶體中為物件分配空間,並返回一個指向該物件的指標 delete:接受一個動態物件的指標,銷燬該物件,並釋放與之關聯的記憶體 動態記憶體的使用需要十分小心,因為要在程式設計的時候要

淺談智慧指標auto_ptr/shared_ptr/unique_ptr

一.智慧指標 1.引入 我們通常使用類似new申請一塊空間,交由一個指標指向,假如說最後忘記delete,將會造成記憶體洩露。而智慧指標的出現,就是對這種問題的解決方式,智慧指標類似指標,卻可以用於管理動態分配的記憶體。本章所解說的是三種智慧指標: (1)C++98提

C++ 智慧指標(unique_ptr, shared_ptr)的原始碼分析

shared_ptr 標頭檔案 template <typename T> class SharedPointer { public: SharedPointer(T *ptr = nullptr, const std::function<void

智慧指標--scoped_ptr shared_ptr weak_ptr

所謂智慧指標就是智慧/自動化的管理指標所指向的動態資源的釋放; scopde_ptr--防拷貝 什麼叫做防拷貝?就是不允許一個地址空間的指標賦值給另一個指標,導致有兩個指標指向同一個地址;也就是說防拷貝能保證地址與指標是一一對應的關係; 實現方法:要拷貝,就需要通過物件來呼

智慧指標shared_ptr 的使用

一、智慧指標 1. 什麼是智慧指標 簡單地說,C++智慧指標是包含過載運算子的類,其行為像常規指標,但智慧指標能夠及時、妥善地銷燬動態分配的資料,並實現了明確的物件生命週期,因此更有價值。 2. 常規指標存在的問題 C++在記憶體分配、釋放和管理方面向程式猿提供了全面的靈

C++ 智慧指標shared_ptr/weak_ptr)原始碼分析

C++11目前已經引入了unique_ptr, shared_ptr, weak_ptr等智慧指標以及相關的模板類enable_shared_from_this等。shared_ptr實現了C++中的RAII機制,它不僅僅具有一般指標(build-in/raw)的特性,更重要的是它可以自動管理使用者

智慧指標shared_ptr的實現)

1.實現原理:shared_ptr是利用一個計數器,無論我們使用拷貝建構函式、賦值運算子過載、作為函式返回值、或作為引數傳給一個引數時計數器+1, 當shared_ptr被賦予一個新值或者需要銷燬時,計數器–,直到計數器為0時,呼叫解構函式,釋放物件,並銷燬其記憶體。shaerd_ptr

C++11 智慧指標std::shared_ptr/std::unique_ptr/std::weak_ptr

std::shared_ptr std::shared_ptr 是一種智慧指標,它能夠記錄多少個 shared_ptr 共同指向一個物件,從而消除顯示的呼叫 delete,當引用計數變為零的時候就

C++:智慧指標shared_ptr

1.智慧指標 C++中用new來動態分配記憶體,delete手動釋放記憶體來達到動態管理記憶體的目的。因為保證在正確的時間釋放記憶體是非常困難的,忘記釋放記憶體就會產生記憶體洩露。 為了更安全、便捷的使用動態記憶體,C++11標準庫提供了新的智慧指標類來管理

智慧指標shared_ptr)的簡單實現

1、原理: shared_ptr允許拷貝和賦值,其底層實現是以"引用計數"為基礎的,通過引用計數來控制空間的釋放,每一個shared_ptr的拷貝都指向相同的記憶體。當有新的指標指向這塊空間時,內部的引用計數加1,每析構一次,內部的引用計數減1,直到引用計數減為0時自動刪除

簡單的計數智慧指標實現

template<class T> class myCountPtr{ public: T *p_re; int *count; public: explicit myCountPtr(T *p=0)

boost庫在工作(9)引用計數智慧指標shared_ptr之二

接著下來,需要演示一下怎麼樣在多個物件裡共享一個物件,而不管每個物件的生命週期,就可以及時地把使用的物件在合適地方刪除。下面的例子裡先定義兩個類,然後每個類都引用共享的物件,接著使用完成後,就會在解構函式裡刪除,可以從例子程式執行的輸出結果看到內部執行的生命週期。有了共享智慧

c++11: 智慧指標 shared_ptr & unique_ptr

一、背景 1. 堆記憶體、棧記憶體、靜態區記憶體 我們知道,靜態記憶體用來儲存區域性 static 物件、類 static 資料成員以及定義在函式之外的變數。而棧記憶體用來儲存定義在函式內的非 static 物件。 分配在靜態區或棧記憶體中的物件由編譯器自動建立和銷燬,對於棧

智慧指標shared_ptr的實現

#include <iostream> using namespace std; template <class T> class smart { public: smart() = default; smart(T* ptr = nullptr):_ptr(

c++ 之四大智慧指標 std::auto_ptr std::shared_ptr std::unuque std::weak_ptr 比較總結

1. 動態記憶體必要性 程式不知道自己需要多少物件;  程式不知道物件的準確型別;  程式需要在多個物件之間共享資料; 2. 動態記憶體在哪裡 程式有靜態記憶體、棧記憶體。靜態記憶體用來儲存區域性static物件、類static資料成員以及定義在任何函式之外的變數

C++Boost庫學習之智慧指標 shared_ptr

目錄 1.共享指標shared_ptr ^   使用例子 ^ #include<boost/shared_ptr.hpp> using namespace boost; using std::cout; using std::endl; str

C++智慧指標shared_ptr講解與使用

手動管理的弊端 在簡單的程式中,我們不大可能忘記釋放 new 出來的指標,但是隨著程式規模的增大,我們忘了 delete 的概率也隨之增大。在 C++ 中 new 出來的指標,賦值意味著引用的傳遞,當賦值運算子同時展現出“值拷貝”和“引用傳遞”兩種截然不同的語義