弱引用是相對於強引用而言,它引用一個對象但是又不控制對象的生存時期,然後在使用時需要先檢查引用的對象是否還存在。而強引用,一般是基於引用計數,引用計數明確的控制了對象的生存時期。如果按控制關系排一個順序,就是:強引用控制對象生存時期,對象生存時期控制弱引用是否有效,弱引用則什麼也沒控制,它本身就是一個對象(例如C#裡的System.WeakReference),高級一點可以是個模板。 弱引用貌似是各種高級語言中的神器,不過只要想費一點功夫,C++語言也可以實現,本文實現的版本是一個模板。弱引用實現的關鍵在於,當對象被刪除時,需要及時的改變弱引用的狀態,這需要引用的對象本身實現這些功能。因此為了實現弱引用,必須規定一個支持弱引用的基類,然後繼承它的類都可以支持弱引用。這個類設計出來大概是這個樣子: h文件代碼 [cpp] view plaincopy struct WeakRefObj { private: void* _internal; protected: WeakRefObj(); public: ~WeakRefObj(); void add_weak_ref(WeakRefObj** ref_ptr); void release_weak_ref(WeakRefObj** ref_ptr, const bool clear_ref_ptr = false); }; 既然是基類,總是希望盡可能的簡潔,尤其希望極少甚至沒有成員變量聲明在頭文件裡。不過研究了很久,總是不能避免添加成員變量,為了不讓頭文件暴漏太多內容,只聲明了一個void* _internal,實際內容在實現代碼中才能取得。 弱引用的關鍵功能通過 add_weak_ref 和 release_weak_ref 實現,add_weak_ref 是用來記下一個WeakRefObj* 變量的地址,通過記錄變量地址 WeakRefObj**,這個變量就變成形式上的弱引用了。release_weak_ref 是把一個WeakRefObj* 變量的地址從記錄中刪除,使它還原為普通的變量。還有一個關鍵的函數,析構函數,其中要對已經記錄的變量進行清零,這樣當對象被刪除後,所有被當做弱引用的變量都變成空值。在高級語言中,這個操作可能是垃圾回收器在某個時刻延時完成的,但在C++裡必須在析構時全部清零。這樣,弱引用的所有功能就算實現了。 不用多說,這裡面有一大堆多線程同步問題要解決,不過先不考慮這些,暫時允許代碼不是線程安全的。下面的實現過程中,記錄的變量地址需要由 WeakRefObj 本身來保存,最簡單的就是用std::set<T> 容器,void* _internal 可以指向一個std::set<WeakRefObj**>。 cpp文件代碼 [cpp] view plaincopy #include"WeakRefObj.h" #include<set> WeakRefObj::WeakRefObj { this->_internal = new std::set<WeakRefObj**>(); } WeakRefObj::~WeakRefObj { if(this->_internal != NULL) { std::set<WeakRefObj**>* weak_refs = (std::set<WeakRefObj**>*)this->_internal; for(std::set<WeakRefObj**>::iterator i = weak_refs->begin(), i_end = weak_refs->end(); i != i_end; i++) { (*(*i)) = NULL; } delete weak_refs; } } void WeakRefObj::add_weak_ref(WeakRefObj** ref_ptr) { std::set<WeakRefObj**>* weak_refs; if((*ref_ptr) != NULL) { weak_refs = (std::set<WeakRefObj**>*)(*ref_ptr)->_internal; weak_refs->erase(ref_ptr); } weak_refs = (std::set<WeakRefObj**>*)this->_internal; weak_refs->insert(ref_ptr); (*ref_ptr) = this; } void WeakRefObj::release_weak_ref(WeakRefObj** ref_ptr, const bool clear_ref_ptr) { std::set<WeakRefObj**>* weak_refs = (std::set<WeakRefObj**>*)this->_internal; weak_refs->erase(ref_ptr); if(clear_ref_ptr) { (*ref_ptr) = NULL; } } 這裡實現弱引用的方法和boost還是有區別的,boost中的弱引用是依賴一個強引用,就是需要有一個智能指針,而這裡實現的弱引用和強引用無關,只跟變量和引用對象有關系。不過,如果只靠這兩個函數管理變量地址來表示弱引用,代碼寫起來實在是比較晦澀,即使保證了 add_weak_ref 和 release_weak_ref 成對使用,有時也很難分清一個變量是否變成弱引用了。所以,我們應該像高級語言一樣,再封裝出一個弱引用對象出來。這時模板就派上用場了,可以寫一個弱引用模板把 add_weak_ref 和 release_weak_ref 封裝起來。用戶可以選擇是使用模板還是親自使用變量。 [cpp] view plaincopy template<typename T> struct TWeakRefPtr { private: WeakRefObj* m; public: TWeakRefPtr(): m(0) { }; TWeakRefPtr(const TWeakRefPtr& value): m(0) { if(value.m != 0) value.m->add_weak_ref(&this->m); }; TWeakRefPtr(T* ptr_t): m(0) { if(dynamic_cast<WeakRefObj*>(ptr_t) != 0) static_cast<WeakRefObj*>(ptr_t)->add_weak_ref(&this->m); }; ~TWeakRefPtr() { if(this->m != 0) this->m->release_weak_ref(&this->m); }; operator T*() { return dynamic_cast<T*>(this->m); }; operator const T*() const { return dynamic_cast<const T*>(this->m); }; TWeakRefPtr& operator =(T* ptr_t) { if(this->m != 0) this->m->release_weak_ref(&this->m); if(dynamic_cast<WeakRefObj*>(ptr_t) != 0) { static_cast<WeakRefObj*>(ptr_t)->add_weak_ref(&this->m); } else { this->m = 0; } return (*this); }; TWeakRefPtr& operator =(const TWeakRefPtr& value) { if(this->m != 0) this->m->release_weak_ref(&this->m); if(value.m != 0) { value.m->add_weak_ref(&this->m); } else { this->m = 0; } return (*this); }; bool operator ==(const TWeakRefPtr& value) const { return (this->m == value.m); }; bool operator ==(T* ptr_t) const { return (this->m == static_cast<WeakRefObj*>(ptr_t)); }; bool operator !=(const TWeakRefPtr& value) const { return (this->m != value.m); }; bool operator !=(T* ptr_t) const { return (this->m != static_cast<WeakRefObj*>(ptr_t)); }; bool operator <(const TWeakRefPtr& value) const { return (this->m < value.m); }; T* operator ->() const { return static_cast<T*>(this->m); }; }; 模板沒法封裝,必須在頭文件裡寫完,雖然代碼很多,不過關鍵功能都是靠 add_weak_ref 和 release_weak_ref 實現,這兩個函數的實現代碼是隱藏在cpp文件裡的,也算是封裝了。 如果考慮線程安全,要做的事就多了,最簡單的,Windows平台可以用 CRITICAL_SECTION,WeakRefObj對象可以維護一個CRITICAL_SECTION,不僅使管理弱引用變得線程安全,還可以加兩個成員函數: sync_lock() 調用EnterCriticalSection, sync_unlock() 調用LeaveCriticalSection, 這樣順便讓整個對象甚至繼承的對象都是線程安全的。 從系統設計上來看,弱引用使代碼邏輯看起來比強引用更和諧。強引用管理對象可靠,但是不能克服循環引用的頑疾,弱引用對此是一個彌補。出現循環引用的根本原因是接口內部實現者之間發生了強引用,而這個強引用並不是使用者親自增加的,所以使用者沒把它當做一次引用。從系統設計上來說,應該是只有接口的使用者有權管理強引用,接口的內部實現者如果需要相互引用,應該全都是弱引用。當使用者通過強引用釋放接口時,內部實現者通過檢查弱引用是否失效作出正確的處理,就可以避免引用循環造成的悲劇。 按照這樣的想法,接口的實現者最好既支持強引用又支持弱引用,因此這個WeakRefObj可以繼續改進,最後成為一個通用的接口基類interface_type。由於包含了一個 void* _internal 成員變量,最終形成的類不算真正意義上的接口,不過 _internal 是私有且無明確類型,繼承後也無法訪問到,然後可以通過protected關鍵字隱藏構造函數和析構函數,使接口公開後既不被能構造也不能被析構,只能通過強引用(引用計數)管理接口的生存時期,同時支持弱引用,這樣作為接口基類還勉強可以接受。其實通過重載 new 操作符返回偏移地址,或者像高級語言一樣在某個堆空間裡保存所有創建出的對象,這個 void* _internal 也是可以消除的,不過由此帶來的多線程安全問題和性能下降都很嚴重,所以就保留現狀了。如何去除這個唯一成員變量暫時沒思路了。 最後貼上接口基類的部分代碼,實現過程太多就省略cpp文件了,貼出頭文件以表明設計思路。強引用,弱引用,聚合接口查詢,都支持了,強引用和弱引用都是以模板類型實現,而且各自可以獨立使用。測試了一下兩種避免聲明成員變量的方法,在堆空間做統一管理會效率低下,重載new函數又會限制上層的開發自由,所以最後無奈還是選擇聲明一個成員變量,不過也懶得起名字了,用一個下劃線表示,不希望被注意到,就這樣好了。 interface_type.h [cpp] view plaincopy #ifndef __INTERFACE_TYPE_H #define __INTERFACE_TYPE_H //接口唯一標識類型。 typedef long long TYPEID; const TYPEID TYPE_NULL = 0x0000000000000000; //接口函數返回類型。 typedef short RESULT; const RESULT RESULT_OK = 0x0000; const RESULT ERR_UNKNOWN = 0xffff; const RESULT ERR_CONV_TYPE_FAILED = 0x1001; const RESULT ERR_ACCESS_NULL_ADDR = 0x1002; //接口類型基類。 struct interface_type { private: void* _; //隱藏此函數以禁止創建實例數組。 void* operator new[](const size_t arr_size) throw(RESULT); //隱藏此函數以禁止刪除實例數組。 void operator delete[](void* arr_addr) throw(RESULT); protected: //構造函數(僅繼承類型可調用)。 interface_type(); //析構函數(僅繼承類型可調用)。 virtual ~interface_type(); public: //使當前線程安全訪問接口。 void sync_lock(); //結束當前線程的安全訪問。 void sync_unlock(); //增加一次強引用。這將使強引用計數增加。 void add_safe_ref(); //把普通地址變量轉換為弱引用變量。轉換後的變量成為實例的一個弱引用。 void add_weak_ref(interface_type** ref_ptr); //減少一次強引用。這將使強引用計數減少,當強引用計數減少至零時,對象就立刻被刪除,相關的任何弱引用也將一起失效。 void safe_release(); //把弱引用變量轉換為普通地址變量。轉換後的變量被還原成普通的地址變量。 void weak_release(interface_type** ref_ptr, const bool clear_ref_ptr = false); //請求指定的聚合接口。繼承接口並重寫此函數,以取得其它接口。 virtual RESULT query_type_t(const TYPEID type, interface_type **result) { //僅演示取得"interface_type"類型。 if(type == TYPE_NULL) { if(result != 0) { this->add_safe_ref(); if((*result) != 0) { (*result)->safe_release(); } (*result) = static_cast<interface_type*>(this); return RESULT_OK; } else { return ERR_ACCESS_NULL_ADDR; } } else { return ERR_CONV_TYPE_FAILED; } }; }; //繼承終止模板。 template<typename T> struct sealed_t { friend T; private: sealed_t() { }; sealed_t(const sealed_t&) { }; }; //接口的強引用類型。 template<typename T> struct safe_ref_t { private: interface_type* m; public: safe_ref_t(): m(0) { }; safe_ref_t(const safe_ref_t& value): m(value.m) { if(this->m != 0) this->m->add_safe_ref(); }; safe_ref_t(T* ptr_t): m(dynamic_cast<interface_type*>(ptr_t)) { if(this->m != 0) this->m->add_safe_ref(); }; ~safe_ref_t() { if(this->m != 0) this->m->safe_release(); }; operator T*() { return dynamic_cast<T*>(this->m); }; operator const T*() const { return dynamic_cast<const T*>(this->m); }; safe_ref_t& operator =(T* ptr_t) { if(dynamic_cast<interface_type*>(ptr_t) != 0) static_cast<interface_type*>(ptr_t)->add_safe_ref(); if(this->m != 0) this->m->safe_release(); this->m = dynamic_cast<interface_type*>(ptr_t); return (*this); }; safe_ref_t& operator =(const safe_ref_t& value) { if(value.m != 0) value.m->add_safe_ref(); if(this->m != 0) this->m->safe_release(); this->m = value.m; return (*this); }; bool operator ==(const safe_ref_t& value) const { return (this->m == value.m); }; bool operator ==(T* ptr_t) const { return (this->m == static_cast<interface_type*>(ptr_t)); }; bool operator !=(const safe_ref_t& value) const { return (this->m != value.m); }; bool operator !=(T* ptr_t) const { return (this->m != static_cast<interface_type*>(ptr_t)); }; bool operator <(const safe_ref_t& value) const { return (this->m < value.m); }; T* operator ->() const { return static_cast<T*>(this->m); }; }; //接口的弱引用類型。 template<typename T> struct weak_ref_t { private: interface_type* m; public: weak_ref_t(): m(0) { }; weak_ref_t(const weak_ref_t& value): m(0) { if(value.m != 0) value.m->add_weak_ref(&this->m); }; weak_ref_t(T* ptr_t): m(0) { if(dynamic_cast<interface_type*>(ptr_t) != 0) static_cast<interface_type*>(ptr_t)->add_weak_ref(&this->m); }; ~weak_ref_t() { if(this->m != 0) this->m->weak_release(&this->m); }; operator T*() { return dynamic_cast<T*>(this->m); }; operator const T*() const { return dynamic_cast<const T*>(this->m); }; weak_ref_t& operator =(T* ptr_t) { if(this->m != 0) this->m->weak_release(&this->m); if(dynamic_cast<interface_type*>(ptr_t) != 0) { static_cast<interface_type*>(ptr_t)->add_weak_ref(&this->m); } else { this->m = 0; } return (*this); }; weak_ref_t& operator =(const weak_ref_t& value) { if(this->m != 0) this->m->weak_release(&this->m); if(value.m != 0) { value.m->add_weak_ref(&this->m); } else { this->m = 0; } return (*this); }; bool operator ==(const weak_ref_t& value) const { return (this->m == value.m); }; bool operator ==(T* ptr_t) const { return (this->m == static_cast<interface_type*>(ptr_t)); }; bool operator !=(const weak_ref_t& value) const { return (this->m != value.m); }; bool operator !=(T* ptr_t) const { return (this->m != static_cast<interface_type*>(ptr_t)); }; bool operator <(const weak_ref_t& value) const { return (this->m < value.m); }; T* operator ->() const { return static_cast<T*>(this->m); }; }; #endif