程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++實現對象和接口的弱引用

C++實現對象和接口的弱引用

編輯:C++入門知識

  弱引用是相對於強引用而言,它引用一個對象但是又不控制對象的生存時期,然後在使用時需要先檢查引用的對象是否還存在。而強引用,一般是基於引用計數,引用計數明確的控制了對象的生存時期。如果按控制關系排一個順序,就是:強引用控制對象生存時期,對象生存時期控制弱引用是否有效,弱引用則什麼也沒控制,它本身就是一個對象(例如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    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved