原文: http://blog.chinaunix.net/uid-26881496-id-3175819.html
ECLARE_DYNAMIC/IMPLEMENT_DYNAMIC宏的詳細解釋
最近有些朋友在看《深入淺出MFC》的時候,被第三章的幾個宏給卡住了,記得我第一次看此書時,也被這幾個宏給卡住。當然真正卡人的其實是第一個,也就是 DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC。我做了一個詳解,供同樣被卡住的朋友做個參考:)
說明:這兩個宏的主要目的,是在所指定的class(比如CView)的聲明和實現裡,加上一些靜態成員函數和靜態成員變數。所以,不要管“\”這個換行標誌,就把裡面的每一句話當作是在類裡的聲明或者是實現。另外,“##”和"#"符號我就不不贅述了。
另外,建議結合《深入淺出MFC》這本書,JJHou寫了一些展開宏的執行個體,結合執行個體看這個解釋可能效果會比較好一點:)
DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC
#define DECLARE_DYNAMIC(class_name)\
public:\
static CRuntimeClass class##class_name;\
//聲明一個類型為CRuntimeClass的靜態public成員變數,變數名是由字串"class"
//與所指定的類的類名組成。舉例而言,如果你寫DECLARE_DYNAMIC(CMyView),則等於聲明了一個
// static CRuntimeClass classCMyView靜態變數
virtual CRuntimeClass* GetRuntimeClass() const;\
//聲明一個虛函數,函數名為GetRuntimeClass,傳回值為CRuntimeClass類型的指標
//無參數,並且是個const函數
#define IMPLEMENT_DYNAMIC(class_name,bass_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;\
//定義一個C類型字串靜態變數,變數名由"_lpsz"和指定類的類名組成,變數值為該指定類型的名字
//比如是CMyView,那麼定義的就是static char _lpszCMyView="CMyView";
CRuntimeClass class_name::class##class_name = {\
_lpsz##class_name,sizeof(class_name),wSchema,pfnNew,\
RUNTIME_CLASS(base_class_name),NULL};\
//給之前在DECLARE_DYNAMIC裡定義的CRuntimeClass類型的靜態成員變數賦值
//當然,除最後一個m_pNextClass沒有賦值(賦值為NULL,它由下面的結構處理)
static AFX_CLASSINIT _init_##class_name(&class_name::class##class_name);\
//初始化一個名為"_init_##class_name"的AFX_CLASSINIT靜態結構,主要作用是給指定的class_name的
//class##class_name靜態變數的最後一個成員m_pNextClass賦值,具體見下面解釋AFX_CLASSINIT中
CRuntimeClass* class_name::GetRuntimeClass
() const\
{ return &class_name::class##class_name;}\
//之前在DECLARE_DYNAMIC裡定義的GetRuntimeClass的實現,很簡單,就一個return語句。
#define RUNTIME_CLASS(class_name)\
(&class_name::class##class_name)
//這部分之所以單獨define出一個宏,主要是為了方便從某個指定的class直接得到它的CRuntimeclass靜態成員
//以下是解釋AFX_CLASSINIT結構,注意,這不是一個宏,請回本貼就可以看下面內容
本帖隱藏的內容需要回複才可以瀏覽
[ 本帖最後由 成為高手 於 2008-10-29 08:49 編輯 ]
學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所引入的一樣的四個