天天看點

探察RUNTIME_CLASS之類的秘密

學mfc學到文檔,視圖和架構的時候,知道必須在這三個類的派生類的類聲明 

裡加上DECLARE_DYNCREATE,然後在類聲明外合适的地方加上IMPLEMENT_DYNCREA 

TE,然後文檔,視圖和架構,還有文檔模闆就可以協調工作了。檢視msdn,發現 

類似的宏有這幾對: 

DECLARE_DYNAMIC 和 IMPLEMENT_DYNAMIC 

DECLARE_DYNCREATE 和 IMPLEMENT_DYNCREATE 

DECLARE_SERIAL 和 IMPLEMENT_SERIAL 

雖然msdn裡介紹了他們的作用,但對于它們為什麼會起這樣的作用心裡卻沒 

底,于是翻了翻mfc的源代碼,喜歡鑽牛角尖的人可以和我一起來鑽一鑽。 

1。 

RUNTIME_CLASS宏的定義是這樣的: 

#define RUNTIME_CLASS(class_name) 

((CRuntimeClass*)(&class_name::class##class_name)) 

其中##的意思是把##兩邊的符号都進行宏擴充(如果它們是宏的話),然後把擴充 

後的内容連接配接在一起,中間不加空格。例如:RUNTIME_CLASS(CView)将被擴充成 

: 

(CRuntimeClass*)(&CView::classCView) 

但這個classCView是什麼意思?原來,classCView是由DECLARE_DYNAMIC(CView) 

引入的一個public屬性的CRuntimeClass類型的靜态成員變量: 

static const AFX_DATA CRuntimeClass classCView; 

原來RUNTIME_CLASS的作用就是引用由DECLARE_DYNAMIC宏引入的靜态成員變 

量。 

2。DECLARE_DYNAMIC(class_name) 

由于篇幅的原因,宏的具體定義代碼就不列出來了,感興趣的可以去看檔案 

afx.h。 

該宏往類中聲明了三個成員: 

protected: 

static CRuntimeClass* PASCAL _GetBaseClass(); 

public: 

virtual CRuntimeClass* GetRuntimeClass() const; 

static const AFX_DATA CRuntimeClass class##class_name; 

有兩個成員函數,一個靜态成員變量class+類名,同RUNTIME_CLASS相似,如 

果是DECLARE_DYNAMIC(CView)的話,這個靜态成員變量将是classCView。可見這 

個成員變量的名稱是和DECLARE_DYNAMIC的參數有關的。在下文我們把這個成員變 

量統統記做class##class_name。 

這個靜态成員和兩個成員函數在哪裡被初始化和具體實作呢?原來是在IMPL 

EMENT_DYNAMIC宏裡。 

3。IMPLEMENT_DYNAMIC(class_name, base_class_name) 

檢視它的宏定義,如果_AFXDLL被定義了的話,由DECLARE_DYNAMIC引入的成 

員的初始化和實作是這樣的: 

CRuntimeClass* PASCAL class_name::_GetBaseClass() 

return RUNTIME_CLASS(base_class_name); 

CRuntimeClass* class_name::GetRuntimeClass() const 

return RUNTIME_CLASS(class_name); 

AFX_COMDAT const AFX_DATADEF 

CRuntimeClass class_name::class##class_name = 

#class_name, 

sizeof(class class_name), 

0xFFFF, 

NULL, 

NULL, 

&class_name::_GetBaseClass, 

NULL 

};//這是在初始化靜态成員變量class##class_name。 

//CRuntimeClass結構的各個成員的意義可檢視msdn。 

4。_DECLARE_DYNAMIC(class_name) 

該宏的定義和DECLARE_DYNAMIC(class_name)基本一樣。不同之處是靜态成員 

class##class_name前面沒有const修飾符。 

5。DECLARE_DYNCREATE(class_name) 

該宏也往類中引入了DECLARE_DYNAMIC宏所引入的那三個成員。除此之外,它 

還另外引入了一個成員: 

static CObject* PASCAL CreateObject(); 

該宏引入的成員在IMPLEMENT_DYNCREATE裡初始化和實作。 

6。IMPLEMENT_DYNCREATE(class_name, base_class_name) 

該宏自然是初始化和實作由DECLARE_DYNCREATE引入的成員了。 

我們看看CreateObject的實作: 

CObject* PASCAL class_name::CreateObject() 

return new class_name; 

呵,這個函數是如此簡單,它就是用CObject類裡重載的new操作符建立一個 

該類類型的對象。 

7。_DECLARE_DYNCREATE(class_name) 

該宏引入了和DECLARE_DYNCREATE引入的四個成員差不多的成員。唯一的差別 

是該宏引入的靜态成員class##class_name前面沒有const修飾符。 

8。DECLARE_SERIAL(class_name) 

該宏引入了和_DECLARE_DYNCREATE所引入的一樣的四個成員,另外它還多了 

這麼一句: 

AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* & 

pOb); 

原來是把重載操作符operator>>的函數當作該類的友元。于是在操作符函數oper 

ator>>中就可以通路該類的成員了。 

9。IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) 

該宏初始化了成員變量: 

CRuntimeClass class_name::class##class_name= 

#class_name, 

sizeof(class class_name), 

wSchema, 

class_name::CreateObject), 

RUNTIME_CLASS(base_class_name), 

NULL 

};//在這裡,class##class_name前面是沒有const修飾符的。 

該宏還實作了下列函數: 

CObject* PASCAL class_name::CreateObject() 

return new class_name; 

CRuntimeClass* class_name::GetRuntimeClass() const 

return RUNTIME_CLASS(class_name); 

CArchive& AFXAPI operator>> 

(CArchive& ar, class_name* &pOb) 

pOb=(class_name*)ar.ReadObject(RUNTIME_CLASS(class_name)); 

return ar; 

該宏還聲明了一個函數原型: 

AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name));