在現代C++編程中,智能指針給我們在資源管理上帶來了很多好處,這裡就不多說了。
在工作中,我們常常會用智能指針來管理資源,其中最常用的就是引用計數類智能指針了(shared_ptr)。
資源共享型的智能指針有兩種實現,一種是侵入式,一種是非侵入式。
在教材裡比較常見的是非侵入式的,它的實現完全放在智能指針模板裡,模板類有一個用於保存資源類對象的指針變量,和一個用於記錄資源對象使用計數的指針變量,這兩個東西是所有的智能指針對象共享的,所以通過指針保存。

而侵入式則不同,它的實現分散在智能指針模板和使用智能指針模板的類中:模板類只有一個用於保存對象的指針變量,對象的計數放在了資源類中。
vc+436OsyrnTw8TatObSsrHIvc/J2aOs0rKxyL3PsLLIq6O7PC9wPg0KPHA+MqGi0vLOqtL908O8xsr9tOa0otTattTP87G+ye2jrMv50tTU2rqvyv2199PDtcTKsbryv8nS1NaxvdO0q7Xd18rUtLbUz/O12Na3o6y2+LK708O1o9DE0v3Tw7zGyv3WtbaqyqejqLfHx9bI68q91sfE3Na41eu21M/ztcS/vbG0o6yx2NDrtPjXxdbHxNzWuNXrxKOw5aOst/HU8r7Nu+Gz9s/WttTP89L908O8xsr9tqrKp6OpoaM8L3A+DQo8cD7IsbXjysejujwvcD4NCjxwPjGhotfK1LTA4LHY0OvT0NL908O8xsr9seTBv6OssqLH0rjDseTBv7XE1Pa89b/J0tSxu8fWyOvKvdbHxNzWuNXrxKOw5bv5wOCy2df3o6zV4s/UtcPC6bezo7s8L3A+DQo8cD4yoaLI57n7uMPA4LKisrvP68q508PWx8Tc1rjV66Osy/y7ucrHu+G0+NfF0v3Tw7zGyv2x5MG/oaM8L3A+DQo8cD7B7c3io6zWx8Tc1rjV69PQ0ru49s7et6ix3MPitcTOyszio6y+zcrH0a27t9L908OhozwvcD4NCjxoMiBpZD0="侵入式智能指針實現">侵入式智能指針實現
兩個要點:
1.將引用計數變量從資源類中抽離出來,封裝成一個基類,該基類包含了引用計數變量。如果一個類想使用智能指針,則只需要繼承自該基類即可;
2.引用計數的基類,設計成模板類,接受引用計數類型作為參數,比如使用int類型或者原子計數類型作為引用計數變量。默認情況下應該使用原子計數類型作為引用計數變量。
3.引用計數基類的構造函數、拷貝構造函數、析構函數應為protected,即該類只能通過繼承關系被使用。
4.拷貝構造函數並不拷貝引用計數基類的數據成員,而是重新將原子計數_atomic置為0——因為每個對象都有一個自己的引用計數,當發生對象拷貝構造時,新的對象的計數應該置為0,而不應該拷貝舊對象的計數。
5.賦值操作operator=,比如A=B,同上面一樣,只對資源類的成員進行拷貝,而不拷貝其引用計數基類的數據成員。也就是說,將B的值賦給A,A的引用計數應該保持不變,而不能將B的引用計數拷貝過來——這是對象的拷貝,而不是智能指針的拷貝。
首先實現引用計數基類(注:Atomic的實現這裡就不給出了):
/** * @brief 智能指針基類. * * 所有需要智能指針支持的類都需要從該對象繼承, * * 內部采用引用計數Atomic實現,對象可以放在容器中; */ templateclass HandleBaseT { public: /** 原子計數類型*/ typedef T atomic_type; /** * @brief 復制。引用計數不能被復制。 * * @return HandleBase& */ HandleBaseT& operator=(const HandleBaseT&) { return *this; } /** * @brief 增加計數 */ void incRef() { _atomic.inc_fast(); } /** * @brief 減少計數, 當計數==0時, 且需要刪除數據時, 釋放對象 */ void decRef() { if(_atomic.dec_and_test() && !_bNoDelete) { _bNoDelete = true; delete this; } } /** * @brief 獲取計數. * * @return int 計數值 */ int getRef() const { return _atomic.get(); } /** * @brief 設置不自動釋放. * * @param b 是否自動刪除,true or false */ void setNoDelete(bool b) { _bNoDelete = b; } protected: /** * @brief 構造函數 */ HandleBaseT() : _atomic(0), _bNoDelete(false) { } /** * @brief 拷貝構造,_atomic和_bNoDelete不能被拷貝,只能重置 */ HandleBaseT(const HandleBaseT&) : _atomic(0), _bNoDelete(false) { } /** * @brief 析構 */ virtual ~HandleBaseT() { } protected: /** * 計數 */ atomic_type _atomic; /** * 是否自動刪除 */ bool _bNoDelete; }; // 針對int類型計數變量的特化 // 在類聲明中定義的函數將自動inline,類外定義的函數需顯式inline template<> inline void HandleBaseT ::incRef() { ++_atomic; } template<> inline void HandleBaseT ::decRef() { if(--_atomic == 0 && !_bNoDelete) { _bNoDelete = true; delete this; } } template<> inline int HandleBaseT ::getRef() const { return _atomic; } // 默認使用Atomic作為引用計數類型 typedef HandleBaseT HandleBase;
以上實現了計數基類,所有需要使用智能指針的對象必須繼承自該類。
智能指針模板類的實現需要關注的幾個點:
1.初始化、賦值等操作需要考慮參數是原始指針、其他類型的智能指針初、用同一類型的智能指針三種情況。
2.需要重載<、=、!=幾個操作(非成員函數),左右操作數使用兩個模板參數。
3.賦值操作需要檢查自我賦值。
/**
* @brief 空指針異常
*/
struct Shared_PtrNull_Exception : public Exception
{
Shared_PtrNull_Exception(const string &buffer) : Exception(buffer){};
~Shared_PtrNull_Exception() throw(){};
};
/**
* @brief 智能指針模板類.
*
* 可以放在容器中,且線程安全的智能指針.
*
* 通過它定義智能指針,該智能指針通過引用計數實現,
*
* 可以放在容器中傳遞.
*
* template T必須繼承於HandleBase
*/
template
class Shared_Ptr
{
public:
/**
* 元素類型
*/
typedef T element_type;
/**
* @brief 用原生指針初始化, 計數+1.
*
* @param p
*/
Shared_Ptr(T* p = 0)
{
_ptr = p;
if(_ptr)
{
_ptr->incRef();
}
}
/**
* @brief 用其他智能指針r的原生指針初始化, 計數+1.
*
* @param Y
* @param r
*/
template
Shared_Ptr(const Shared_Ptr& r)
{
_ptr = r._ptr;
if(_ptr)
{
_ptr->incRef();
}
}
/**
* @brief 拷貝構造, 計數+1.
*
* @param r
*/
Shared_Ptr(const Shared_Ptr& r)
{
_ptr = r._ptr;
if(_ptr)
{
_ptr->incRef();
}
}
/**
* @brief 析構
*/
~Shared_Ptr()
{
if(_ptr)
{
_ptr->decRef();
}
}
/**
* @brief 賦值, 普通指針.
*
* @param p
* @return Shared_Ptr&
*/
Shared_Ptr& operator=(T* p)
{
if(_ptr != p)
{
if(p)
{
p->incRef();
}
T* ptr = _ptr;
_ptr = p;
if(ptr)
{
ptr->decRef();
}
}
return *this;
}
/**
* @brief 賦值, 其他類型智能指針.
*
* @param Y
* @param r
* @return Shared_Ptr&
*/
template
Shared_Ptr& operator=(const Shared_Ptr& r)
{
if(_ptr != r._ptr)
{
if(r._ptr)
{
r._ptr->incRef();
}
T* ptr = _ptr;
_ptr = r._ptr;
if(ptr)
{
ptr->decRef();
}
}
return *this;
}
/**
* @brief 賦值, 該類型其他執政指針.
*
* @param r
* @return Shared_Ptr&
*/
Shared_Ptr& operator=(const Shared_Ptr& r)
{
if(_ptr != r._ptr)
{
if(r._ptr)
{
r._ptr->incRef();
}
T* ptr = _ptr;
_ptr = r._ptr;
if(ptr)
{
ptr->decRef();
}
}
return *this;
}
/**
* @brief 將其他類型的智能指針換成當前類型的智能指針.
*
* @param Y
* @param r
* @return Shared_Ptr
*/
template
static Shared_Ptr dynamicCast(const Shared_Ptr& r)
{
return Shared_Ptr(dynamic_cast(r._ptr));
}
/**
* @brief 將其他原生類型的指針轉換成當前類型的智能指針.
*
* @param Y
* @param p
* @return Shared_Ptr
*/
template
static Shared_Ptr dynamicCast(Y* p)
{
return Shared_Ptr(dynamic_cast(p));
}
/**
* @brief 獲取原生指針.
*
* @return T*
*/
T* get() const
{
return _ptr;
}
/**
* @brief 調用.
*
* @return T*
*/
T* operator->() const
{
if(!_ptr)
{
throwNullHandleException();
}
return _ptr;
}
/**
* @brief 引用.
*
* @return T&
*/
T& operator*() const
{
if(!_ptr)
{
throwNullHandleException();
}
return *_ptr;
}
/**
* @brief 是否有效.
*
* @return bool
*/
operator bool() const
{
return _ptr ? true : false;
}
/**
* @brief 交換指針.
*
* @param other
*/
void swap(Shared_Ptr& other)
{
std::swap(_ptr, other._ptr);
}
protected:
/**
* @brief 拋出異常
*/
void throwNullHandleException() const;
public:
T* _ptr;
};
/**
* @brief 拋出異常.
*
* @param T
* @param file
* @param line
*/
template inline void
Shared_Ptr::throwNullHandleException() const
{
throw Shared_PtrNull_Exception("shared_ptr null handle error");
}
/**
* @brief ==判斷.
*
* @param T
* @param U
* @param lhs
* @param rhs
*
* @return bool
*/
template
inline bool operator==(const Shared_Ptr& lhs, const Shared_Ptr& rhs)
{
T* l = lhs.get();
U* r = rhs.get();
if(l && r)
{
return *l == *r;
}
else
{
return !l && !r;
}
}
/**
* @brief 不等於判斷.
*
* @param T
* @param U
* @param lhs
* @param rhs
*
* @return bool
*/
template
inline bool operator!=(const Shared_Ptr& lhs, const Shared_Ptr& rhs)
{
T* l = lhs.get();
U* r = rhs.get();
if(l && r)
{
return *l != *r;
}
else
{
return l || r;
}
}
/**
* @brief 小於判斷, 用於放在map等容器中.
*
* @param T
* @param U
* @param lhs
* @param rhs
*
* @return bool
*/
template
inline bool operator<(const Shared_Ptr& lhs, const Shared_Ptr& rhs)
{
T* l = lhs.get();
U* r = rhs.get();
if(l && r)
{
return *l < *r;
}
else
{
return !l && r;
}
}
class Test: public HandleBase
{
// ...
};
Shared_Ptr myTestClass = new Test();