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安全(也就是引用計數維護正確),對於原始對象操作依賴於用戶。