C++深度探索系列:智慧指標(Smart Pointer) [二] (轉)

worldblog發表於2007-12-13
C++深度探索系列:智慧指標(Smart Pointer) [二] (轉)[@more@]

  深度探索智慧指標(Smart Pointer)

主題:

一、剖析C++標準庫智慧指標(std::auto_ptr)
 
  1.Do you Smart Pointer?
  2.std::auto_ptr的設計原理
  3.std::auto_ptr高階使用指南
  4.你是否覺得std::auto_ptr還不夠完美?

二、C++條件,尋找構造更強大的智慧指標(Smart Pointer)的
  策略
 
  1.支援引用記數的多種設計策略
  2.支援處理多種資源
  3.支援Subclassing
  4.支援多執行緒條件下,執行緒的多種設計策略
  5.其它多種特殊要求下,再構造

三、Generic Programming基礎技術和Smart Pointer
  1.回首處理資源中的Traits技術
  2.回首多執行緒支援的設計


四、COM實現中,Smart Pointer設計原理


五、著名C++庫(標準和非標準)中的Smart Pointer現狀

---------------------------------------------------------------------

二、C++條件,尋找構造更強大的智慧指標(SmartPointer)的策略 
 
 
   1.支援引用記數的多種設計策略
 
  你聽說過COM和它著名的IUnknown介面吧?
  IUnknown是幹什麼的?我要告訴你,IUnknown介面三個簽名中,
  兩個是用來管理(CoClass ,類物件)的記數來控制
  它的生命週期的.
 
  在實踐中,我們的物件並不是只用一次,只允許一個引用的.

  那麼,誰來管理它的生命週期呢?
 
  我們的策略是:引用記數. 當物件的引用記數為零時,就銷燬物件.
  在沒有託管環境的情況下,事實上,銷燬物件的往往還是auto_ptr.
  而COM中,銷燬物件的是物件自己.
 
  事實上,它和我們的智慧指標不是一個級別上的概念.
  我們的智慧指標負責的是物件級的引用.而COM是以介面引用為
  核心的.保證介面操作時,介面引用記數的自動管理.
 
  哦!是的!那麼我們怎樣給auto_ptr加上物件引用記數的功能?

  策略1:
 
  一個物件對應一個引用記數物件.
  智慧指標以記數物件為.
  想象,這又歸到經典的"新增中間層"解決方案上了.
 
  # 核心一:
 
  我們新增一個 "引用記數class".
  它的職責有二:
  a.維護物件的引用記數.
  b.維護物件的指標.
 
  結構示意如下:
  template
  class ObfCounted{
  private:
  T* m_OBJ_Delegate_Ptr;
  unsigned int m_UIcounted;
  public:
  explicit ObjRefCounted(T* m_Paramin = 0):
  m_UIcounted(1), m_OBJ_Delegate_Ptr(m_Paramin){}; 
   
  template ObjRefCounted(ObjRefCounted& x) {
  m_OBJ_Delegate_Ptr = x.m_OBJ_Delegate_Ptr);  };
 
    ObjRefCounted(const ObjRefCounted& x):m_UIcounted
  (x.m_UIcounted), m_OBJ_Delegate_Ptr(x.m_ObjDelegate_Ptr){};
  ~ObjRefCounted();
 
  void ReleaseRef ();
  void AddRef ();
  T* GetRealPointer () const;
  };
 
  # 核心二
  在智慧指標中維護一個引用記數class的指標
  template
  class SmartPointer{
  public:
  ObjRefCounted* _m_ObjRefCounted;
  .....
  .....
  };
 
  透過上面的兩個策略,我們就可以在智慧指標構造時,為之付上一個
  引用記數物件.這個物件負責託管Smart Pointer原本應該維護
  的物件指標.並且負責最終消除物件.

  在Smart Pointer中,我們將會涉及大量的_m_ObjRefCounted的操作.
  下面簡敘一過程,詳細不訴,自己設計之.
  譬如:當你將一個物件指標賦給Smart Pointer將構建一輔助的
  引用記數託管物件,此時m_UIcounted為1,m_OBJ_Delegate_Ptr被賦
  以物件指標,假如現在我又將Smart Pointer 賦給另一SmartPointer2
  , 那麼SmartPointer2_m_ObjRefCounted->ReleaseRef();
  減少原來維護的物件的記數,將自己的_m_ObjRefCounted置為
  SmartPointer2依附的記數物件,再呼叫_m_ObjRefCounted->AddRef();
  OK!就是這樣的.


  策略2.
  在每一個智慧指標內部維護一個物件指標和一個引用記數值的
  的指標.
 
  這裡的重點在於維護一個引用記數值的指標,
  它使得Smart Pointer之間保持一致的記數值成為可能.
 
  結構示意如下:
  template
  class SmartPointer{
  private:
  T* m_ObjPtr;
  unsigned int* RefCounted;
  public:
  explicit SmartPoint(T* PARAMin = 0) : m_ObjPtr(PARAMin),
  RefCounted(new int(1)) { }
  SmartPoint(const SmartPoint& PARAMin2):
  m_ObjPtr(PARAMin2.m_ObjPtr),
  RefCounted(PARAMin2.RefCounted) { ++*RefCounted; }
  ....
  ...
  };
 
  不過這個方法的擴充套件性很差.
  因為引用記數功能結合到Smart Pointer中去了.
  一般不會用這種方法.
 
  以上面的兩種策略為基礎,根據實際情況,可設計出更多的記數方法.
 
 
  2.利用Traits(Partial Specialization)技術,
  支援處理多種資源
 
  在no1中,我們提到不可讓auto_ptr管理陣列,那是因為
  auto_ptr構析函式中呼叫的是delete的緣故.
  陣列不可,其它的如,控制程式碼、執行緒控制程式碼等當然更不可以了.

  下面我們就這個問題來探討:

  策略1.
  透過函式指標來支援多種資源的處理.
  我們的智慧指標將設計成具有兩個引數的模板類.
  第一個引數指示:資源的型別
  第二個引數指示:處理資源的函式型別
 
  結構示意如下:

  typedef void FreeReFunction(void* p);
  void DealSingleObject(void* p); 
  void DealArray(void* p);
  void DealFile(void* p);
  //
  //  針對特殊的資源加入函式指標宣告
  //
  template
  class SmartPointer{ 
  public:
  ~SmartPointer(){ DealFunction(); }
  ...
  ...
  /* Other codes */
  };

  inline void DealSingle(void* p)
  { 
  if(p)  delete p;
  }

  inline void DealArray(void* p){
  if(p)  delete[] p; 
  }
 
  inline void DealFile(void* p){
  if(p)  p->close();
  } 
  //
  //針對特殊資源加入處理函式
  // 

  oK!但是我們在使用這個策略的時候,一定要注意,
  傳遞進的指標不能是錯誤的,這個你必須保證.
  當然對上面的結構示意再改造,使之具有更強的
  辨錯能力也是可取的.

   3.支援Subclassing

  關於智慧指標中的Subclassing,是什麼?
  我們先來看一程式片段:
 
  class BaseClass {};
  class Derived : public BaseClass {};
 
  auto_ptr m_Derived;
 auto_ptr m_Base;
 
 auto_ptr pDerived = new Derived;
 m_Base = pDerived;
   //
  //m_Derived = (PDerived&)m_Base;  //#1
  //

  看到上面的#1沒有,你認為在auto_ptr中,
  它或者同等語義的行為可以?
  不可以.為什麼?
  它本質上,相當與這樣的操作:
  BaseClass* m_BaseClass;
  m_BaseClass = new DerivedClass(inParam);
  這顯然是的.
 
  在上面我們曾經,auto_ptr對具有虛擬特性的類,
  也能體現出虛擬性.

  然而那並不能訪問繼承的資料,實現的不是真正意義
  上的SubClassing.

  那麼,我們這樣來實現這樣的功能.
 
  策略1.
  在上述引用記數部分敘述的SmartPoint中,我們作如下的操作:
 
  template SmartPointer& operator = (const SmartPointer& that)
  {
  if (m_pRep ! = reinterpret_cast* > (that.m_pRep))
  {
  ReleaseRef ();
  m_pRep = reinterpret_cast* > (that.m_pRep);
  AddRef ();
  }
  return *this;
  }
  };

  不錯,reinterpret_cast,就是它幫我們解決了問題.

  策略2.
  關於第二種方法,這裡不再詳細敘說.
  它涉及太多的細節,峰迴路轉的很難說清.
  大體上,它是利用引用記數物件中維護的物件指標為void*
  而在具體的呼叫是透過static_cast或reinterpret_cast轉化.
  總之,所謂的SubClassing技術離不開轉化.

  4.支援多執行緒條件下,執行緒安全的多種設計策略
 
  對於標準C++,多執行緒問題並不很受關注.
  原因在於目前,標準庫並不支援多執行緒.
 
  策略1:
  首先我們想到:對資料進行訪問同步.
  那麼,我們有兩種方案:
  a. 建立一個臨界區物件.將物件的執行傳遞給臨界區物件.
  以保證安全.
  b.利用臨時物件來完成任務,將臨界的責任留給被作用物件.
 
  下面分析第二種的做法:
  programme1:
  class Widget
  {
  ...
  void Lock();  //進入臨界區
  void Unlock(); //退出臨界區
  };
 
  programme2:
  template
  class Locking
  {
  public:
  LockingProxy(T* pObj) : pointee_ (pObj)
  { pointee_->Lock(); }
  //  在臨時物件構造是就鎖定
  //  weight物件(臨界區).
  ~LockingProxy() { pointee_->Unlock(); }
  // 
  //  在臨時物件銷燬時,退出臨界區.
  //
  T* operator->() const
  { return pointee_; }
  //
  //  這裡過載->運算子.將對臨時物件的方法執行
  //  請求轉交給weight物件
  //
  private:
  LockingProxy& operator=(const LockingProxy&);
  T* pointee_;
  };

  programme3:
  template
  class SmartPtr
  {
  ...
  LockingProxy operator->() const
  { return LockingProxy(pointee_); }
  //
  //  核心就在這裡:產生臨時物件
  //  LockingProxy(pointee_)
  private:  sT* pointee_;
  };

  Programme4.
  SmartPtr sp = ...;
  sp->DoSomething();  //##1

  下面,我們模擬一下,執行的過程.
  ##1執行時,構建了臨時物件LockingProxy(pointee_)
  此物件在構造期間就鎖定Weight物件,並將DoSomethin()
  方法傳遞給weight物件執行,在方法執行完,臨時物件消失,
  構析函式退出臨界區.

  4.其它特殊要求下的再構造
 
  a.回首當年,你是否覺的
  auto_ptr m_SMPTR = new x(100);
  居然通不過.不爽!
  No problem !
  auto_ptr(T* m_PARAMin = 0) shrow() : m_Tp(m_PARAMin){}
  解決問題.
 
  b. Consr it:
  void fook(x* m_PARAMin){};
  可是我只有auto_ptr m_SMPTR;
  No problem !
  T* operator T*(auto_ptr& m_PARAMin) throw ()
  { return m_Tp; }
 
  fook(m_SMPTR); // ok !  now
  c.事實上,你可以根據自己的需要.
  過載更多或加入功能成員函式.

--------------------------------------------------------------
  待續

三、Generic Programming基礎技術和Smart Pointer
  1.回首處理資源中的Traits技術
  2.回首多執行緒支援的設計


四、COM實現中,Smart Pointer設計原理


五、著名C++庫(標準和非標準)中的Smart Pointer現狀

--------------------------------------------------------------


--------------------------------------------------------------
  鄭重宣告:
  允許複製、修改、傳遞或其它行為
  但不準用於任何商業用途.
  寫於  20/3/
  最後修改: 20/3/2003
  By RedStar81
  to:81_RedStar@163.com">81_RedStar@163.com
-------------------------------------------------------------


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992991/,如需轉載,請註明出處,否則將追究法律責任。

相關文章