智能指針shared_ptr【無鎖設計基於GCC】


1. shared_ptr 介紹

  使用過Boost的話對shared_ptr一定有很深的印象。多個shared_ptr指向同一個對象,每個shared_ptr會使對象的引用計數加+1,當引用計數為0時,

對象將被析構。本文實現一個簡潔版本的shared_ptr,並沒有太多跨平台特性,實現代碼可以再GCC上運行。

      本文中的引用計數由ref_count_t類實現,參見下文的詳細分析。

  詳文另見:

  代碼詳見:http://ffown.sinaapp.com/?p=49

  svn co http://ffown.googlecode.com/svn/trunk/fflib/lib

2.  shared_ptr 的構造

     我們期望shared_ptr的行為盡量的接近原始指針的行為。所以shared_ptr應該支持三種構造方式

  a. 空構造類似與void* p =NULL;

      b. shared_ptr可以通過原始對象指針構造,類似於void* p = q;

  c. shared_ptr 可以通過已存在的shared_ptr構造。

  首先shared_ptr是一個模板類,其由連個屬性。

private:
object_t* m_dest_ptr;
ref_count_t* m_ref_count;
};

其中m_dest_ptr 指向目標對象, m_ref_count 用來記錄該對象的引用計數。為了簡單,shared_ptr類遵循一個原則m_dest_ptr和m_ref_count  同時為NULL,或同時不為NULL。

其中 object_t 為模板類型的別名。

template<typename T>
class shared_ptr_t
{
public:
typedef T object_t;
typedef shared_ptr_t<T> self_type_t;

  1> 空構造目標對象和引用計數默認都為空。

template<typename T>
shared_ptr_t<T>::shared_ptr_t(object_t* p):
m_dest_ptr(p),
m_ref_count(NULL)
{
if (NULL != m_dest_ptr)
{
m_ref_count = new ref_count_t();
}
}

  share_ptr_t<int> p;

  2> 支持原始對象指針作為構造函數參數

template<typename T>
shared_ptr_t<T>::shared_ptr_t(object_t* p):
m_dest_ptr(p),
m_ref_count(NULL)
{
if (NULL != m_dest_ptr)
{
m_ref_count = new ref_count_t();
}
}

  用例:share_ptr_t<int> p(new int());

  3> 使用已存在的shared_ptr 構造

  

template<typename T>
shared_ptr_t<T>::shared_ptr_t(self_type_t& p):
m_dest_ptr(p.get()),
m_ref_count(p.ger_ref_count())
{
if (NULL != m_dest_ptr)
{
m_ref_count->inc();
}
}

  用例: share_ptr_t<int> q(p);

3. shared_ptr 獲取引用計數或原始指針

  有時需要知道shared_ptr當前引用計數的值,通過shared_ptr獲取原始指針理所當然。So:

size_t       ref_count() const       { return m_ref_count != NULL? (size_t)m_ref_count->size(): 0; }
object_t*    get() const             { return m_dest_ptr; }

所以很容易驗證shared_ptr的行為:

shared_ptr_t p(new int());
assert(p.ref_count() == 1);
shared_ptr_t<int> q(p);
assert(q.ref_count() == 1);

4. 減少引用計數

  shared_ptr需要顯示的析構對象,所以提供reset接口,當目標對象已經創建並且引用計數達到零時(即不再有shared_ptr保存目標對象的控制權),析構目標對象。

template<typename T>
void shared_ptr_t<T>::reset()
{
if (m_dest_ptr)
{
if (true == m_ref_count->dec_and_check_zero())
{
delete m_ref_count;
delete m_dest_ptr;
}
m_ref_count = NULL;
m_dest_ptr = NULL;
}
}

5. shared_ptr 的析構

  很簡單,減少引用計數。

template<typename T>
shared_ptr_t<T>::~shared_ptr_t()
{
reset();
}

6. 向原始指針一樣使用shared_ptr

  可以這樣使用shared_ptr

  struct foo_t { int a; }

  shared_ptr_t<foo_t> p(new foo_t());

  (*p).a = 100;

  p->a = 100;

  if(p) cout << "p not null!\n";

     所以提供如下接口:

template<typename T>
typename shared_ptr_t<T>::object_t& shared_ptr_t<T>::operator*()
{
assert(NULL != m_dest_ptr);
return *m_dest_ptr;
}

template<typename T>
typename shared_ptr_t<T>::object_t* shared_ptr_t<T>::operator->()
{
assert(NULL != m_dest_ptr);
return m_dest_ptr;
}

operator bool() const
{
  return NULL != m_dest_ptr;
}

7. Lock Free引用計數實現

  GCC中已經定義了一些atomic operation,但是查閱資料后,應該是對Intel的平台支持較好,其他平台支持不確定。故把atomic操作封裝成宏。

#define ATOMIC_ADD(src_ptr, v)                         (void)__sync_add_and_fetch(src_ptr, v)
#define ATOMIC_SUB_AND_FETCH(src_ptr, v) __sync_sub_and_fetch(src_ptr, v)

  ref_count_t 實現很簡單:

class ref_count_t
{
typedef volatile long atomic_t;
public:
ref_count_t():
m_ref_num(1)
{}
~ref_count_t()
{}

inline void inc()
{
ATOMIC_ADD(&m_ref_num, 1);
}
inline bool dec_and_check_zero()
{
return 0 == ATOMIC_SUB_AND_FETCH(&m_ref_num, 1);
}
inline atomic_t size()
{
return m_ref_num;
}

private:
atomic_t m_ref_num;
};
#endif

8. 線程安全性

1. 單線程多個shared_ptr指向不同的對象,安全。

2. 單線程多個shared_ptr指向相同的對象,安全。

3. 多線程多個操作不同的shared_ptr, 指向不同的對象,安全。

4. 多線程多個操作不同的shared_ptr, 指向相同對象,shared_ptr安全(也就是引用計數維護正確),對於原始對象操作依賴於用戶。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM