Skin技術實現架構(五)

來源:互聯網
上載者:User

接上篇,控制項類的介面有了:CWidgetHookBase,產生控制項對象的工廠也有了,下面就該實現控制項類了。在上篇定義控制項基類的時候,我們只定義了一個抽象函數Install,而沒有任何其他代碼,那麼,所有的實現代碼都交給各個控制項類去實現嗎?不是的,這些控制項類還有許多公用代碼可以在基類實現,但是,我們選擇不在CWidgetHookBase中加入這些代碼,而是再加入一個中間類:CWidgetHook。為什麼不把這兩個基類合成一個類呢?其實,最初的設計是只有一個基類的,就是CWidgetHook,而我們又希望在繼承類中使用WTL對Windows控制項的封裝類,這樣,根據ATL/WTL的架構,CWidgetHook就必須是一個模板類,而模板類是不能作為基類指標的,因為模板是類型不定的,而我們的抽象原廠模式要求一個基類指標,所以我們又提取出CWidgetHookBase這樣的純虛的介面類。結果就是如所示的結構:

下面是CWidgetHook模板類的聲明:

///////////////////////////////////////////////////////////////////

/// Base class for all widget hook

/// Parameters:

/// T

///      Derived class

/// TBase

///      Widget window wrapper, use WTL wrappers for convenient

template <class T, class TBase = CWindow>

class CWidgetHook : public CWidgetHookBase, public CWindowImpl<T, TBase>

這裡用了多重繼承,也是ATL裡常用的,第一個父類是我們前面定義的CWidgetHookBase介面,我們需要這個介面來實現抽象工廠設計模式。第二個父類是CWindowImp,這是ATL定義的,是所有視窗類別的高層父類(雖然還不是頂層)。
CWindowImp接收兩個模板參數,同時也是CWidgetHook的模板參數。第一個模板參數是繼承類,這也是ATL中常用的技巧,這個技巧使得我們可以在父類中知道繼承類的類型,於是,把this指標強制轉換成繼承類的類型,就可以調用繼承類的方法,這種方式實現了類似於虛函數的多態,卻不需要付出虛函數的效能代價。這也算把C++模板運用到及至了吧,好像只有在ATL中有這種用法,STL中有很多其他的看起來近似古怪的技巧,這些其實正是C++的魅力所在。後來的語言,如java,C#雖然也在各方面都有新的進步,但比起C++,真正值得人興奮的地方還真很難找出來。
扯遠了,繼續說我們的第二個模板參數。追蹤ATL的代碼,可以看到其實TBase參數最終是作為基類的,通過模板參數改變基類,這也是模板之於OO不同的地方。TBase有一個預設值CWindow,可以在這裡傳入其他類,但必須是CWindow的繼承類,最有價值的參數當然是WTL視窗封裝類,這樣,在實現控制項訊息處理類的時候,就可以使用WTL封裝類提供的函數,而不需要只依賴windows API了,確實可以帶來不少協助。
才講完CWidgetHook的聲明,真夠羅嗦的。下面來看CWidgetHook的定義和實現,首先看幾個函式宣告:

     void Initialize() {}; //instance initialize

     void Finalize() {};    //instance finalize

     static void InitializeClass() {}; //class initialize

     static void FinalizeClass() {};  //class finalize

上面四個函數都包含了空的實現,在繼承類中可以選擇的重載它們。重載非虛函數,利用上面講到的模板技巧實作類別似虛函數的多態,這就是模板給我們帶來的新概念。Initialize在每個執行個體產生時被調用,Finalize在執行個體銷毀前被調用;靜態函數InitializeClass在類的第一個執行個體產生時調用,FinalizeClass在類的最後一個執行個體銷毀時調用。
為什麼要有兩個靜態函數呢,因為一個類代表同一種視窗,這些視窗會使用同樣的資源,例如checkbox,需要幾張不同狀態的圖片,而這些圖片對於每個checkbox來說都是一樣的,如果為每個執行個體儲存這樣一份資源,就有點浪費記憶體了。對於一個Skin外掛程式來說,效率還是很重要的,所以我們選擇用靜態變數保持這些圖片,並且在第一個checkbox產生的時候載入這些資源,後續產生的其他checkbox可以重用這些資源,然後在最後一個checkbox消失的時候釋放這些資源,這樣,記憶體的使用量被最佳化到最小。
這兩個靜態函數的調用在建構函式和解構函式中,另外還有一個執行個體計數值m_lRef配合,這裡就不多說了。下面還剩下OnFinalMessage和Install函數沒講。
OnFinalMessage比較簡單,調用Fianlize並刪除自己,由於是CWindowImp繼承類,OnFinalMessage函數會在視窗銷毀的時候被自動調用,這樣,就保證了執行個體會自己釋放,不會造成記憶體流失。另外值得一提的是:在調用Finalize等上面提到的四個函數時,都是先把this指標轉換成繼承類T的指標pT,然後在pT指標上調用。這樣才能實作類別似虛函數的多態,這種方式據說比使用虛函數的方式效率高,不過我這樣寫倒不是因為要刻意提高效率,只不過,That's the ATL way
最後來看Install函數,這是在CWidgetHookBase中定義的純虛函數,在CWidgetHook模板類中,這個函數得以實現。Install函數的主要功能是調用SubclassWindow,從而獲得對視窗訊息的控制。另外,還實現了控制訊息反射和初始化執行個體的操作。下面是它的代碼:

     virtual void Install(HWND hWidget)

     {

         ATLASSERT(::IsWindow(hWidget));

 

         SubclassWindow(hWidget);

        

         //if it is a child window, install a reflector for its parent

         //so that the parent will reflect messages back to me

         if ( (::GetWindowLong(hWidget, GWL_STYLE) & WS_CHILD) == WS_CHILD)

         {

              HWND hWndParent = ::GetParent(hWidget);

              ATLASSERT(::IsWindow(hWndParent));

              //the WM_GETREFLECTOR get the installed reflector, if exists

              CReflectHook* pReflector =

                   (CReflectHook*)::SendMessage(hWndParent, WM_GETREFLECTOR, 0, 0);

              if (!pReflector)

                   new CReflectHook(hWndParent);

         }

 

         T* pT = static_cast<T*>(this);

         pT->Initialize();

     }

中間的一大塊代碼都是關於訊息反射的,留到下次在講,今天寫的夠長的了,最後講一下Initialize吧。其實這裡可以看到一個設計模式:模板方法。Install函數是模板方法,它調用的Initialize方法則要在繼承類中重載。那麼,Initialize方法是執行個體的初始化函數,為什麼不放在建構函式裡呢?因為每個執行個體的初始化可能不太一樣,要根據被掛鈎的視窗的狀態決定,所以,必須等到SubclassWindow被調用之後,才調用Initialize方法,在繼承類的Initialize實現中,可以通過m_hWnd直接獲得視窗控制代碼,調用API或者WTL封裝類方法檢查視窗狀態,並執行必要的執行個體初始化代碼。
好了,今天差不多了,剩點尾巴下次講

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.