首先,如果你不知道什麼是智能指針,請先移步:
C++智能指針簡單剖析
#ifndef AUTO_PTR_H
#define AUTO_PTR_H
template
class auto_ptr
{
public :
//使用explicit關鍵字避免隱式轉換
explicit auto_ptr(T* p=0);
~auto_ptr();
//使用另一個類型兼容的auto_ptr來初始化一個新的auto_ptr
template
auto_ptr(auto_ptr& rhs);
template
auto_ptr& operator=(auto_ptr& rhs);
T& operator*() const;
T* operator->() const;
//返回原始對象的指針
T* get() const;
//放棄指針的所有權
T* release();
//刪除原有指針並獲得指針p的所有權
void reset(T* p=0);
private:
T* pointee;
};
template
auto_ptr::auto_ptr(T* p)
:pointee(p)
{}
template
auto_ptr::~auto_ptr()
{
delete pointee; //如果所有權被轉移了,我們會將指針置0,delete空指針不會發生任何事
}
template
template
auto_ptr::auto_ptr(auto_ptr& rhs)
:pointee(rhs.release()) //轉交所有權,即rhs將所有權轉交給this,並將自身指針置0
{}
template
template
auto_ptr& auto_ptr::operator=(auto_ptr& rhs)
{
if(this!=&rhs)
reset(rhs.release());
return *this;
}
template
T& auto_ptr::operator*() const
{
return *pointee;
}
template
T* auto_ptr::operator->() const
{
return pointee;
}
template
T* auto_ptr::get() const
{
return pointee;
}
template
T* auto_ptr::release()
{
T* oldpointee=pointee;
pointee=0; //置NULL
return oldpointee; //交出所有權
}
template
void auto_ptr::reset(T* p)
{
if(pointee!=p)
{
delete pointee; //刪除原有的
pointee=p; //設置新的
}
}
#endif
實現原理:
當多個shared_ptr管理同一個指針,僅當最後一個shared_ptr析構時,指針才被delete。為實現這一點,我們需要一個引用計數(reference counting)。引用計數指的是,所有管理同一個裸指針(raw pointer)的shared_ptr,都共享一個引用計數器,每當一個shared_ptr被賦值(或拷貝構造)給其它shared_ptr時,這個共享的引用計數器就加1,當一個shared_ptr析構或者被用於管理其它裸指針時,這個引用計數器就減1,如果此時發現引用計數器為0,那麼說明它是管理這個指針的最後一個shared_ptr了,於是我們釋放指針指向的資源。
在底層實現中,這個引用計數器保存在某個內部類型裡(這個類型中還包含了deleter,它控制了指針的釋放策略,默認情況下就是普通的delete操作),而這個內部類型對象在shared_ptr第一次構造時以指針的形式保存在shared_ptr中。shared_ptr在賦值和拷貝構造另一個shared_ptr時,這個指針被另一個shared_ptr共享。在引用計數歸零時,這個內部類型指針與shared_ptr管理的資源一起被釋放。此外,為了保證線程安全性,引用計數器的加1,減1操作都是原子操作,它保證shared_ptr由多個線程共享時不會出問題。
下面看看一個例子:
shared_ptr 是引用計數型(reference counting)智能指針,它在堆(heap)上放了個計數值(count)。shared_ptr 包含兩個成員,一個是指向 Foo 的指針 ptr,另一個是 ref_count 指針(不一定是原始指針,有可能是 class 類型),指向堆上的 ref_count 對象。ref_count 對象有多個成員,具體的數據結構如圖所示,其中 use_count為我們所說的計數,deleter 和 allocator 是可選的。
shared_ptr安全級別:
一個 shared_ptr 對象實體可被多個線程同時讀取;
兩個 shared_ptr 對象實體可以被兩個線程同時寫入,“析構”算寫操作;
如果要從多個線程讀寫同一個 shared_ptr 對象,那麼需要加鎖。
具體的代碼實現這裡不涉及,有興趣的朋友可以去看一看boost的實現~