1 CRuntimeClass和類別型錄網
首先,CRuntimeClass結構和類別型錄網是實現RTTI和動態創建這兩個技術的基礎。
因爲早期技術比較單一,沒有typeid運算符和template等概念,於是我們爲每個類設計了一個對應的CRuntimeClass結構來儲存該類的相關信息,將類庫中所有類的 CRuntimeClass結構放入一個單向鏈表中,就構成了一個類別型錄網,鏈表向上遍歷類層次,直到表尾CObject::classCObject。
1)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指針爲空,也就無法動態創建該類對象。
終於把這兩個技術說了一遍,技術很強大,我希望能用淺顯易懂的方式說給諸位,不當之處請見諒~