在現代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 智能指針模板類. * * 可以放在容器中,且線程安全的智能指針. * * 通過它定義智能指針,該智能指針通過引用計數實現, * * 可以放在容器中傳遞. * * templateT必須繼承於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_PtrmyTestClass = new Test();