MFC第三天——RTTI & Dynamic Creation

上次提到了MFC程序的初始化,今天好好說說運行時類型識別(RTTI)和動態創建技術

1 CRuntimeClass和類別型錄網
   首先,CRuntimeClass結構和類別型錄網是實現RTTI和動態創建這兩個技術的基礎。
   因爲早期技術比較單一,沒有typeid運算符和template等概念,於是我們爲每個類設計了一個對應的CRuntimeClass結構來儲存該類的相關信息,將類庫中所有類的          CRuntimeClass結構放入一個單向鏈表中,就構成了一個類別型錄網,鏈表向上遍歷類層次,直到表尾CObject::classCObject。

 1)CRuntimeClass結構:


struct CRuntimeClass
{
// Attributes
   LPCSTR m_lpszClassName;//類名
   int m_nObjectSize;//對象大小
   UINT m_wSchema; // schema number of the loaded class
   CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
   CRuntimeClass* m_pBaseClass;//指向基類

   CObject* CreateObject();//
   static CRuntimeClass* PASCAL Load();


   // CRuntimeClass objects linked together in simple list
   static CRuntimeClass* pFirstClass; // start of class list指向表頭
   CRuntimeClass* m_pNextClass;    // linked list of registered classes指向鏈表中的下一個類

};

加紅色的三個變量都與動態創建有關,在下面說明。

   2)類別型錄網:


2 DECLARE_DYNAMIC和IMPLEMENT_DYNAMIC宏
  我們用這兩個宏,去在MFC中實現CRuntimeClass的聲明和類別型錄網的構成。
  1)#define DECLARE_DYNAMIC(class_name) 
     public: 
        static CRuntimeClass class##class_name; 
        virtual CRuntimeClass* GetRuntimeClass() const;
   注:DECLARE_DYNAMIC宏聲明瞭一個與Cxxx類相對應的CRuntimeClass結構classCxxx。
  2)#define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
        _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
     #define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
        static char _lpsz##class_name[] = #class_name; \
        CRuntimeClass class_name::class##class_name = { \
                _lpsz##class_name, sizeof(class_name), wSchema, pfnNew, \
                        RUNTIME_CLASS(base_class_name), NULL }; \
        static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name); \
        CRuntimeClass* class_name::GetRuntimeClass() const \
                { return &class_name::class##class_name; } \
    其中,RUNTIME_CLASS宏:
     #define RUNTIME_CLASS(class_name) \
        (&class_name::class##class_name)
     和AFX_CLASSINIT結構:
     struct AFX_CLASSINIT
        { AFX_CLASSINIT(CRuntimeClass* pNewClass); };
     而AFX_CLASSINIT::AFX_CLASSINIT( CRuntimeClass* pNewClass)
     {
        pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;
        CRuntimeClass::pFirstClass = pNewClass ;
     }
   注:IMPLEMENT_DYNAMIC宏,初始化classCxxx結構,並實現了鏈表。


3 RTTI

  有了類別型錄網,現在可以說說類型識別技術了。


例:CView* pView = new CView;

        pView->IsKindOf(RUNTIME_CLASS(CWinApp));

        RUNTIME_CLASS(CWinApp)返回與CWinApp對應的CRuntimeClass結構指針

類型識別其實是靠m_pBaseClass的向上索引,兩個類的CRuntimeClass指針的比較。


4 動態創建

  1)DECLARE_DYNCREATE和IMPLEMENT_DYNCREATE宏

       DECLARE_DYNCREATE:

        #define DECLARE_DYNCREATE(class_name)                   

                   DECLARE_DYNAMIC(class_name) \

                   static CObject* PASCAL CreateObject();

       IMPLEMENT_DYNCREATE宏:

       #define IMPLEMENT_DYNCREATE(class_name, base_class_name) \
        CObject* PASCAL class_name::CreateObject() \
                { return new class_name; } \
       _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \
                class_name::CreateObject)

      注:這兩個宏用於動態創建,且可以看到_DYNCREATE宏包含了_DYNAMIC宏。

  2)動態創建

     在頭文件中加入DECLARE_DYNCREATE,在實現文件中加入IMPLEMENT_DYNCREATE,我們就可以進行動態創建類了。

     我們這裏先把main中的動態創建語句寫出來,反向思考看看動態創建的執行過程。

     void main()

     {

          ...

         CRuntimeClass* pClassRef;

         CObject* pOb;

         while(1)

         {

              if((pClassRef = CRuntimeClass::Load()) == NULL)

                 break;

              pOb = pClassRef -> CreateObject();

              if(pOb != NULL)

                 pOb->sayhello();

         }

     }

     首先執行到CRuntimeClass::Load(),看看load函數定義我們就會知道,它加載了一個類名,然後遍歷類別型錄網,當找到這個類對應的CRuntimeClass結構時,返回該結構的指針,否則返回null。然後到CreateObject(),再看看這個函數定義,它先判斷m_pfnCreateObject指針是否爲空,如果不爲空,我們就調用該指針指向的函數classname::CreateObject()進行對象的創建,並返回對象指針完成動態創建。

  :值得一提的是m_pfnCreateObject指針,它是一個函數指針,保存着相應類的CreateObject()函數地址,而類的CreateObject()函數是在DECLARE_DYNCREATE宏中聲明的(從上面可以看到),而且該函數其實是一個靜態回調函數。所以,如果我們沒有在類中加入DECLARE_DYNCREATE宏,m_pfnCreateObject指針爲空,也就無法動態創建該類對象。


終於把這兩個技術說了一遍,技術很強大,我希望能用淺顯易懂的方式說給諸位,不當之處請見諒~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章