今天在網(wǎng)上突然發(fā)現(xiàn)了下面幾個(gè)關(guān)于c代碼中的宏定義的說(shuō)明,回想下,好像在系統(tǒng)的代碼中也見(jiàn)過(guò)這些零散的定義,但沒(méi)有注意,看到別人總結(jié)了下,發(fā)現(xiàn)果然很有用,雖然不知有的道可用與否,但也不失為一種手段,所以就先把它摘抄下來(lái),增加一點(diǎn)見(jiàn)識(shí):
1,防止一個(gè)頭文件被重復(fù)包含
#ifndef BODYDEF_H
#define BODYDEF_H
//頭文件內(nèi)容
#endif
2,得到指定地址上的一個(gè)字節(jié)或字
#define MEM_B( x ) ( *( (byte *) (x) ) )
#define MEM_W( x ) ( *( (word *) (x) ) )
3,得到一個(gè)field在結(jié)構(gòu)體(struct)中的偏移量
#define FPOS( type, field ) ( (dword) &(( type *) 0)-> field )
4,得到一個(gè)結(jié)構(gòu)體中field所占用的字節(jié)數(shù)
#define FSIZ( type, field ) sizeof( ((type *) 0)->field )
5,得到一個(gè)變量的地址(word寬度)
#define B_PTR( var ) ( (byte *) (void *) &(var) )
#define W_PTR( var ) ( (word *) (void *) &(var) )
6,將一個(gè)字母轉(zhuǎn)換為大寫(xiě)
#define UPCASE( c ) ( ((c) >= ''a'' && (c) <= ''z'') ? ((c) - 0x20) : (c) )
7,判斷字符是不是10進(jìn)值的數(shù)字
#define DECCHK( c ) ((c) >= ''0'' && (c) <= ''9'')
8,判斷字符是不是16進(jìn)值的數(shù)字
#define HEXCHK( c ) ( ((c) >= ''0'' && (c) <= ''9'') ||((c) >= ''A'' && (c) <= ''F'') ||((c) >= ''a'' && (c) <= ''f'') )
9,防止溢出的一個(gè)方法
#define INC_SAT( val ) (val = ((val)+1 > (val)) ? (val)+1 : (val))
10,返回?cái)?shù)組元素的個(gè)數(shù)
#define ARR_SIZE( a ) ( sizeof( (a) ) / sizeof( (a[0]) ) )
11,使用一些宏跟蹤調(diào)試
ANSI標(biāo)準(zhǔn)說(shuō)明了五個(gè)預(yù)定義的宏名。它們是:
_LINE_ (兩個(gè)下劃線),對(duì)應(yīng)%d
_FILE_ 對(duì)應(yīng)%s
_DATE_ 對(duì)應(yīng)%s
_TIME_ 對(duì)應(yīng)%s
_STDC_
宏中"#"和"##"的用法
我們使用#把宏參數(shù)變?yōu)橐粋€(gè)字符串,用##把兩個(gè)宏參數(shù)貼合在一起.
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
Printf(STR(vck)); // 輸出字符串"vck"
printf("%d\n", CONS(2,3)); // 2e3 輸出:2000
當(dāng)宏參數(shù)是另一個(gè)宏的時(shí)候
需要注意的是凡宏定義里有用"#"或"##"的地方宏參數(shù)是不會(huì)再展開(kāi).
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
printf("%s\n", CONS(A, A)); // compile error
這一行則是:
printf("%s\n", int(AeA));
INT_MAX和A都不會(huì)再被展開(kāi), 然而解決這個(gè)問(wèn)題的方法很簡(jiǎn)單. 加多一層中間轉(zhuǎn)換宏.
加這層宏的用意是把所有宏的參數(shù)在這層里全部展開(kāi), 那么在轉(zhuǎn)換宏里的那一個(gè)宏(_STR)就能得到正確的宏參數(shù)
#define STR(s) _STR(s) // 轉(zhuǎn)換宏
#define CONS(a,b) _CONS(a,b) // 轉(zhuǎn)換宏
printf("int max: %s\n", STR(INT_MAX)); // INT_MAX,int型的最大值,為一個(gè)變量 #include<climits>
輸出為: int max: 0x7fffffff
STR(INT_MAX) --> _STR(0x7fffffff) 然后再轉(zhuǎn)換成字符串;
printf("%d\n", CONS(A, A));
輸出為:200
CONS(A, A) --> _CONS((2), (2)) --> int((2)e(2))
"#"和"##"的一些應(yīng)用特例
1、合并匿名變量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous, line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示該行行號(hào);
第一層:ANONYMOUS(static int); --> __ANONYMOUS0(static int, __LINE__);
第二層: --> ___ANONYMOUS1(static int, _anonymous, 70);
第三層: --> static int _anonymous70;
即每次只能解開(kāi)當(dāng)前層的宏,所以__LINE__在第二層才能被解開(kāi);
2、填充結(jié)構(gòu)
#define FILL(a) {a, #a}
enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;
MSG _msg[] = {FILL(OPEN), FILL(CLOSE)};
相當(dāng)于:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};
3、記錄文件名
#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);
4、得到一個(gè)數(shù)值類(lèi)型所對(duì)應(yīng)的字符串緩沖大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof "0x7fffffff"];
這里相當(dāng)于:
char buf[11];
C++提供的編譯預(yù)處理功能主要有以下三種:
(一) 宏定義
(二) 文件包含
(三) 條件編譯
在C++中,我們一般用const定義符號(hào)常量。很顯然,用const定義常量比用define定義常量更好。
在使用宏定義時(shí)應(yīng)注意的是:
(a) 在書(shū)寫(xiě)#define 命令時(shí),注意<宏名>和<字符串>之間用空格分開(kāi),而不是用等號(hào)連接。
(b) 使用#define定義的標(biāo)識(shí)符不是變量,它只用作宏替換,因此不占有內(nèi)存。
(c) 習(xí)慣上用大寫(xiě)字母表示<宏名>,這只是一種習(xí)慣的約定,其目的是為了與變量名區(qū)分,因?yàn)樽兞棵?br> 通常用小寫(xiě)字母。
如果某一個(gè)標(biāo)識(shí)符被定義為宏名后,在取消該宏定義之前,不允許重新對(duì)它進(jìn)行宏定義。取消宏定義使用如下命令:
#undef<標(biāo)識(shí)符>
其中,undef是關(guān)鍵字。該命令的功能是取消對(duì)<標(biāo)識(shí)符>已有的宏定義。被取消了宏定義的標(biāo)識(shí)符,可以對(duì)它重新進(jìn)行定義。
宏定義可以嵌套,已被定義的標(biāo)識(shí)符可以用來(lái)定義新的標(biāo)識(shí)符。例如:
#define PI 3.14159265
#define R 10
#define AREA (PI*R*R)
單的宏定義將一個(gè)標(biāo)識(shí)符定義為一個(gè)字符串,源程序中的該標(biāo)識(shí)符均以指定的字符串來(lái)代替。前面已經(jīng)說(shuō)過(guò),預(yù)處理命令不同于一般C++語(yǔ)句。因此預(yù)處理命令后通常不加分號(hào)。這并不是說(shuō)所有的預(yù)處理命令后都不能有分號(hào)出現(xiàn)。由于宏定義只是用宏名對(duì)一個(gè)字符串進(jìn)行簡(jiǎn)單的替換,因此如果在宏定義命令后加了分號(hào),將會(huì)連同分號(hào)一起進(jìn)行置換。
帶參數(shù)的宏定義
帶參數(shù)的宏定義的一般形式如下:
#define <宏名>(<參數(shù)表>) <宏體>
其中, <宏名>是一個(gè)標(biāo)識(shí)符,<參數(shù)表>中的參數(shù)可以是一個(gè),也可以是多個(gè),視具體情況而定,當(dāng)有多個(gè)參數(shù)的時(shí)候,每個(gè)參數(shù)之間用逗號(hào)分隔。<宏體>是被替換用的字符串,宏體中的字符串是由參數(shù)表中的各個(gè)參數(shù)組成的表達(dá)式。例如:
#define SUB(a,b) a-b
如果在程序中出現(xiàn)如下語(yǔ)句:
result=SUB(2, 3)
則被替換為:
result=2-3;
如果程序中出現(xiàn)如下語(yǔ)句:
result= SUB(x+1, y+2);
則被替換為:
result=x+1-y+2;
在這樣的宏替換過(guò)程中,其實(shí)只是將參數(shù)表中的參數(shù)代入到宏體的表達(dá)式中去,上述例子中,即是將表達(dá)式中的a和b分別用2和3代入。
我們可以發(fā)現(xiàn):帶參的宏定義與函數(shù)類(lèi)似。如果我們把宏定義時(shí)出現(xiàn)的參數(shù)視為形參,而在程序中引用宏定義時(shí)出現(xiàn)的參數(shù)視為實(shí)參。那么上例中的a和b就是形參,而2和3以及x+1和y+2都為實(shí)參。在宏替換時(shí),就是用實(shí)參來(lái)替換<宏體>中的形參。
在使用帶參數(shù)的宏定義時(shí)需要注意的是:
(1)帶參數(shù)的宏定義的<宏體>應(yīng)寫(xiě)在一行上,如果需要寫(xiě)在多行上時(shí),在每行結(jié)束時(shí),使用續(xù)行符 "\"結(jié)
束,并在該符號(hào)后按下回車(chē)鍵,最后一行除外。
(2)在書(shū)寫(xiě)帶參數(shù)的宏定義時(shí),<宏名>與左括號(hào)之間不能出現(xiàn)空格,否則空格右邊的部分都作為宏體。
例如:
#define ADD (x,y) x+y
將會(huì)把"(x,y)x+y"的一個(gè)整體作為被定義的字符串。
(3)定義帶參數(shù)的宏時(shí),宏體中與參數(shù)名相同的字符串適當(dāng)?shù)丶由蠄A括號(hào)是十分重要的,這樣能夠避免
可能產(chǎn)生的錯(cuò)誤。例如,對(duì)于宏定義:
#define SQ(x) x*x
當(dāng)程序中出現(xiàn)下列語(yǔ)句:
m=SQ(a+b);
替換結(jié)果為:
m=a+b*a+b;
這可能不是我們期望的結(jié)果,如果需要下面的替換結(jié)果:
m=(a+b)*(a+b);
應(yīng)將宏定義修改為:
#define SQ(x) (x)*(x)
對(duì)于帶參的宏定義展開(kāi)置換的方法是:在程序中如果有帶實(shí)參的宏(如"SUB(2,3)"),則按"#define"命令行中指定的字符串從左到右進(jìn)行置換。如果串中包含宏中的形參(如a、b),則將程序語(yǔ)句中相應(yīng)的實(shí)參(可以是常量、變量或者表達(dá)式)代替形參,如果宏定義中的字符串中的字符不是參數(shù)字符(如a-b中的-號(hào)),則保留。這樣就形成了置換的字符串。 C++提供的編譯預(yù)處理功能主要有以下三種:
(一) 宏定義
(二) 文件包含
(三) 條件編譯
在C++中,我們一般用const定義符號(hào)常量。很顯然,用const定義常量比用define定義常量更好。
在使用宏定義時(shí)應(yīng)注意的是:
(a) 在書(shū)寫(xiě)#define 命令時(shí),注意<宏名>和<字符串>之間用空格分開(kāi),而不是用等號(hào)連接。
(b) 使用#define定義的標(biāo)識(shí)符不是變量,它只用作宏替換,因此不占有內(nèi)存。
(c) 習(xí)慣上用大寫(xiě)字母表示<宏名>,這只是一種習(xí)慣的約定,其目的是為了與變量名區(qū)分,因?yàn)樽兞棵?br> 通常用小寫(xiě)字母。
如果某一個(gè)標(biāo)識(shí)符被定義為宏名后,在取消該宏定義之前,不允許重新對(duì)它進(jìn)行宏定義。取消宏定義使用如下命令:
#undef<標(biāo)識(shí)符>
其中,undef是關(guān)鍵字。該命令的功能是取消對(duì)<標(biāo)識(shí)符>已有的宏定義。被取消了宏定義的標(biāo)識(shí)符,可以對(duì)它重新進(jìn)行定義。
宏定義可以嵌套,已被定義的標(biāo)識(shí)符可以用來(lái)定義新的標(biāo)識(shí)符。例如:
#define PI 3.14159265
#define R 10
#define AREA (PI*R*R)
單的宏定義將一個(gè)標(biāo)識(shí)符定義為一個(gè)字符串,源程序中的該標(biāo)識(shí)符均以指定的字符串來(lái)代替。前面已經(jīng)說(shuō)過(guò),預(yù)處理命令不同于一般C++語(yǔ)句。因此預(yù)處理命令后通常不加分號(hào)。這并不是說(shuō)所有的預(yù)處理命令后都不能有分號(hào)出現(xiàn)。由于宏定義只是用宏名對(duì)一個(gè)字符串進(jìn)行簡(jiǎn)單的替換,因此如果在宏定義命令后加了分號(hào),將會(huì)連同分號(hào)一起進(jìn)行置換。
帶參數(shù)的宏定義
帶參數(shù)的宏定義的一般形式如下:
#define <宏名>(<參數(shù)表>) <宏體>
其中, <宏名>是一個(gè)標(biāo)識(shí)符,<參數(shù)表>中的參數(shù)可以是一個(gè),也可以是多個(gè),視具體情況而定,當(dāng)有多個(gè)參數(shù)的時(shí)候,每個(gè)參數(shù)之間用逗號(hào)分隔。<宏體>是被替換用的字符串,宏體中的字符串是由參數(shù)表中的各個(gè)參數(shù)組成的表達(dá)式。例如:
#define SUB(a,b) a-b
如果在程序中出現(xiàn)如下語(yǔ)句:
result=SUB(2, 3)
則被替換為:
result=2-3;
如果程序中出現(xiàn)如下語(yǔ)句:
result= SUB(x+1, y+2);
則被替換為:
result=x+1-y+2;
在這樣的宏替換過(guò)程中,其實(shí)只是將參數(shù)表中的參數(shù)代入到宏體的表達(dá)式中去,上述例子中,即是將表達(dá)式中的a和b分別用2和3代入。
我們可以發(fā)現(xiàn):帶參的宏定義與函數(shù)類(lèi)似。如果我們把宏定義時(shí)出現(xiàn)的參數(shù)視為形參,而在程序中引用宏定義時(shí)出現(xiàn)的參數(shù)視為實(shí)參。那么上例中的a和b就是形參,而2和3以及x+1和y+2都為實(shí)參。在宏替換時(shí),就是用實(shí)參來(lái)替換<宏體>中的形參。
在使用帶參數(shù)的宏定義時(shí)需要注意的是:
(1)帶參數(shù)的宏定義的<宏體>應(yīng)寫(xiě)在一行上,如果需要寫(xiě)在多行上時(shí),在每行結(jié)束時(shí),使用續(xù)行符 "\"結(jié)
束,并在該符號(hào)后按下回車(chē)鍵,最后一行除外。
(2)在書(shū)寫(xiě)帶參數(shù)的宏定義時(shí),<宏名>與左括號(hào)之間不能出現(xiàn)空格,否則空格右邊的部分都作為宏體。
例如:
#define ADD (x,y) x+y
將會(huì)把"(x,y)x+y"的一個(gè)整體作為被定義的字符串。
(3)定義帶參數(shù)的宏時(shí),宏體中與參數(shù)名相同的字符串適當(dāng)?shù)丶由蠄A括號(hào)是十分重要的,這樣能夠避免
可能產(chǎn)生的錯(cuò)誤。例如,對(duì)于宏定義:
#define SQ(x) x*x
當(dāng)程序中出現(xiàn)下列語(yǔ)句:
m=SQ(a+b);
替換結(jié)果為:
m=a+b*a+b;
這可能不是我們期望的結(jié)果,如果需要下面的替換結(jié)果:
m=(a+b)*(a+b);
應(yīng)將宏定義修改為:
#define SQ(x) (x)*(x)
對(duì)于帶參的宏定義展開(kāi)置換的方法是:在程序中如果有帶實(shí)參的宏(如"SUB(2,3)"),則按"#define"命令行中指定的字符串從左到右進(jìn)行置換。如果串中包含宏中的形參(如a、b),則將程序語(yǔ)句中相應(yīng)的實(shí)參(可以是常量、變量或者表達(dá)式)代替形參,如果宏定義中的字符串中的字符不是參數(shù)字符(如a-b中的-號(hào)),則保留。這樣就形成了置換的字符串。
解剖MFC自動(dòng)生成的宏定義
一、關(guān)于DECLARE_MESSAGE_MAP宏定義
使用MFC向?qū)В贏pplicationType頁(yè)面選擇DialogBased,生成一個(gè)對(duì)話框項(xiàng)目,Dialog類(lèi)命名為CCapturePacketDlg,在CCapturePacketDlg.cpp中自動(dòng)產(chǎn)生下列代碼:
BEGIN_MESSAGE_MAP(CCapturePacketDlg, CDialog)2
ON_WM_PAINT()3
END_MESSAGE_MAP() - 先來(lái)分析ON_WM_PAINT(),在頭文件“afxmsg.h”有它的宏定義,如下:
#define ON_WM_PAINT() \ 2
{ WM_PAINT, 0 , 0 , 0 , AfxSig_vv, \3
(AFX_PMSG)(AFX_PMSGW) \4
(static_cast < void (AFX_MSG_CALL CWnd:: * )( void ) > ( & ThisClass :: OnPaint)) } , 2.1 #define WM_PAINT 0x000F
2.2 AfxSig_vv = AfxSig_v_v_v
2.2.1 enum AfxSig::AfxSig_v_v_v = 19
3.1 AFX_PMSG:typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void); //為一個(gè)函數(shù)指針
3.2 AFX_PMSGW:typedef void (AFX_MSG_CALL CWnd::*AFX_PMSGW)(void); //為一個(gè)函數(shù)指針
將ON_WM_PAINT()完全展開(kāi):


{2
0x000F, 3
0,4
0,5
0,6
19,7
//Converts OnPaint to the type of CCmdTarget finally. Derive Class 's pointer -> Base Class's pointer8
(AFX_MSG_CALL CCmdTarget::*)((AFX_MSG_CALL CWnd::*)(static_cast< void (AFX_MSG_CALL CWnd::*)(void) >(&ThisClass :: OnPaint))9
} 2. 再來(lái)分析BEGIN_MESSAGE_MAP(CCapturePacketDlg, CDialog),在“afxwin.h”中有定義:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \2
PTM_WARNING_DISABLE \3
const AFX_MSGMAP* theClass::GetMessageMap() const \4

{ return GetThisMessageMap(); } \5
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \6

{ \7
typedef theClass ThisClass; \8
typedef baseClass TheBaseClass; \9
static const AFX_MSGMAP_ENTRY _messageEntries[] = \10

{2.1 PTM_WARNING_DISABLE:
#define PTM_WARNING_DISABLE \
__pragma(warning( push )) \ //#pragma warning( push [ ,n ] ),Where n represents a warning level (1 through 4).
//The pragma warning( push ) stores the current warning state for all warnings.
__pragma(warning( disable : 4867 ))//Do not issue the specified warning message(s).
//http://msdn2.microsoft.com/en-us/2c8f766e.aspx
// Allows selective modification of the behavior of compiler warning messages.
3.1 struct AFX_MSGMAP
{
3.1.1 const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
3.1.2 const AFX_MSGMAP_ENTRY* lpEntries;
};
3.1.2 struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT_PTR nSig; // signature type (action) or pointer to message #
3.1.2.1 AFX_PMSG pfn; // routine to call (or special value)
};
3.1.2.1 typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
5.1 #define PASCAL __stdcall
將BEGIN_MESSAGE_MAP(CCapturePacketDlg, CDialog)完全展開(kāi):
__pragma(warning( push )) __pragma(warning( disable : 4867 ))2
const struct AFX_MSGMAP* CCapturePacketDlg::GetMessageMap() const3

{ 4
return GetThisMessageMap(); 5
}6
const struct AFX_MSGMAP* __stdcall CCapturePacketDlg::GetThisMessageMap()7

{8
typedef CCapturePacketDlg ThisClass; 9
typedef CDialog TheBaseClass; 10
static const struct AFX_MSGMAP_ENTRY _messageEntries[] = 11

{ 3 最后分析END_MESSAGE_MAP(),在“afxwin.h”中有定義:
#define END_MESSAGE_MAP() \2

{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \3
}; \4
static const AFX_MSGMAP messageMap = \5

{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \6
return &messageMap; \7
} \8
PTM_WARNING_RESTORE2.1 AfxSig_end:enum AfxSig.AfxSig_end = 0
2.2 AFX_PMSG:typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);//函數(shù)指針
4.1 struct AFX_MSGMAP
{
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
const AFX_MSGMAP_ENTRY* lpEntries;
};
8.1 #define PTM_WARNING_RESTORE \
__pragma(warning( pop ))
//pop restores the state of all warnings (including 4705, 4706, and 4707) to what it was at the beginning of the code.
·最后將
BEGIN_MESSAGE_MAP(CCapturePacketDlg, CDialog)2
ON_WM_PAINT()3
END_MESSAGE_MAP()
__pragma(warning( push )) __pragma(warning( disable : 4867 ))2
const struct AFX_MSGMAP* CCapturePacketDlg::GetMessageMap() const3

{ 4
return GetThisMessageMap(); 5
}6
const struct AFX_MSGMAP* __stdcall CCapturePacketDlg::GetThisMessageMap()7

{8
typedef CCapturePacketDlg ThisClass; 9
typedef CDialog TheBaseClass; 10
static const struct AFX_MSGMAP_ENTRY _messageEntries[] = 11

{12

{13
0x000F, 14
0,15
0,16
0,17
19,18
//Converts OnPaint to the type of CCmdTarget finally. Derive Class 's pointer -> Base Class's pointer19
(AFX_MSG_CALL CCmdTarget::*)((AFX_MSG_CALL CWnd::*)(static_cast< void (AFX_MSG_CALL CWnd::*)(void) >(&ThisClass :: OnPaint))20
},21
//add others22

{23
0,24
0,25
0,26
0,27
0,28
(AFX_PMSG)029
}30
}31
static const struct AFX_MSGMAP messageMap = 32

{33
&TheBaseClass::GetThisMessageMap,34
&_messageEntries[0]35
};36
return &messageMap;37
}38
__pragma(warning( pop ))39

老辦法查看它的定義:
#define DECLARE_MESSAGE_MAP() \2
protected: \3
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \4
virtual const AFX_MSGMAP* GetMessageMap() const; \小結(jié):
每次用MFC類(lèi)向?qū)梢粋€(gè)類(lèi)時(shí),系統(tǒng)會(huì)在類(lèi)的聲明部分添加兩個(gè)方法的聲明:GetThisMessageMap(),GetMessageMap()。在類(lèi)的實(shí)現(xiàn)部分.cpp文件中加上這兩個(gè)方法的定義。
當(dāng)然這所有的代碼都是由系統(tǒng)生成的,如果我們要定義自己的消息處理函數(shù)呢,例如,我們要添加一個(gè)按鈕(ID為:IDC_BUTTON1)的單擊處理函數(shù)我們可以添加宏ON_NOTIFY(NM_CLICK, IDC_BUTTON1, OnMyClick),OnMyClick為自定義函數(shù),但是他必須與ON_NOTIFY中的函數(shù)原型一致。
二、關(guān)于DECLARE_DYNCREATE宏
使用MFC向?qū)В贏pplicationType頁(yè)面選擇SingleDocument,生成一個(gè)單文檔項(xiàng)目,Document類(lèi)命名為CDynamicDoc,在CDynamicDoc.h中自動(dòng)產(chǎn)生DECLARE_DYNCREATE(CDynamicDoc),CDynamicDoc.cpp中產(chǎn)生IMPLEMENT_DYNCREATE(CDynamicDoc, CDocument)。
1、展開(kāi)CDynamicDoc.h中的DECLARE_DYNCREATE(CDynamicDoc):
// not serializable, but dynamically constructable2
#define DECLARE_DYNCREATE(class_name) \3
DECLARE_DYNAMIC(class_name) \4
static CObject* PASCAL CreateObject();
#ifdef _AFXDLL2
#define DECLARE_DYNAMIC(class_name) \3
protected: \4
static CRuntimeClass* PASCAL _GetBaseClass(); \5
public: \6
static const CRuntimeClass class##class_name; \7
static CRuntimeClass* PASCAL GetThisClass(); \8
virtual CRuntimeClass* GetRuntimeClass() const; \
protected: 2
static CRuntimeClass* PASCAL _GetBaseClass(); 3
public: 4
static const CRuntimeClass classCDynamicDoc; 5
static CRuntimeClass* PASCAL GetThisClass(); 6
virtual CRuntimeClass* GetRuntimeClass() const; 7
static CObject* PASCAL CreateObject();2、展開(kāi)CDynamicDoc.cpp中的IMPLEMENT_DYNCREATE(CDynamicDoc, CDocument):
#define IMPLEMENT_DYNCREATE(class_name, base_class_name) \2
CObject* PASCAL class_name::CreateObject() \3

{ return new class_name; } \4
IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, \5
class_name::CreateObject, NULL)
#define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \2
CRuntimeClass* PASCAL class_name::_GetBaseClass() \3

{ return RUNTIME_CLASS(base_class_name); } \4

AFX_COMDAT const CRuntimeClass class_name::class##class_name =
{ \5
#class_name, sizeof(class class_name), wSchema, pfnNew, \6
&class_name::_GetBaseClass, NULL, class_init }; \7
CRuntimeClass* PASCAL class_name::GetThisClass() \8

{ return _RUNTIME_CLASS(class_name); } \9
CRuntimeClass* class_name::GetRuntimeClass() const \10

{ return _RUNTIME_CLASS(class_name); } \
struct CRuntimeClass2

{3
// Attributes4
LPCSTR m_lpszClassName;5
int m_nObjectSize;6
UINT m_wSchema; // schema number of the loaded class7
CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class8
#ifdef _AFXDLL9
CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();10
#else11
CRuntimeClass* m_pBaseClass;12
#endif13

14
// Operations15
CObject* CreateObject();16
BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;17

18
// dynamic name lookup and creation19
static CRuntimeClass* PASCAL FromName(LPCSTR lpszClassName);20
static CRuntimeClass* PASCAL FromName(LPCWSTR lpszClassName);21
static CObject* PASCAL CreateObject(LPCSTR lpszClassName);22
static CObject* PASCAL CreateObject(LPCWSTR lpszClassName);23

24
// Implementation25
void Store(CArchive& ar) const;26
static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);27

28
// CRuntimeClass objects linked together in simple list29
CRuntimeClass* m_pNextClass; // linked list of registered classes30
const AFX_CLASSINIT* m_pClassInit;31
};
/**//////////////////////////////////////////////////////////////////////////////2
// Basic object model3

4
// generate static object constructor for class registration5
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);6
struct AFX_CLASSINIT7

{ AFX_CLASSINIT(CRuntimeClass* pNewClass)
{ AfxClassInit(pNewClass); } };8
//C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\objcore.cpp Line1579
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)10

{11
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();12
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);13
pModuleState->m_classList.AddHead(pNewClass);14
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);15
}16
//可以將AfxClassInit()函數(shù)的功能簡(jiǎn)單的如下表示:17
AFX_CLASSINIT::AFX_CLASSINIT(CRuntimeClass* pNewClass)18

{19
pNewClass->m_pNextClass = CRuntimeClass::pFirstClass;20
CRuntimeClass::pFirstClass = pNewClass;21
}4.1.3 RUNTIME_CLASS如下定義:
#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
#define AFX_COMDAT __declspec(selectany)“##”——operator (##), which is sometimes called the "merging" operator, is used in both object-like and function-like macros.
4.1.8 _RUNTIME_CLASS如下定義:
#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
1//CDynamicDoc, CDocument->class_name, base_class_name
2 static CObject* PASCAL CDynamicDoc::CreateObject()
3
{
4 return new CDynamicDoc;
5 }
6
7 static CRuntimeClass* PASCAL CDynamicDoc::_GetBaseClass()
8
{
9 return CDocument::GetThisClass()
10 }
11
12 __declspec(selectany) static const CRuntimeClass CDynamicDoc::classCDynamicDoc = 
13
{
14 "CDynamicDoc"
15 , sizeof(class CDynamicDoc)
16 , 0xFFFF
17 , CDynamicDoc::CreateObject
18 , &CDynamicDoc::_GetBaseClass
19 , NULL
20 , NULL
21 };
22
23 static CRuntimeClass* PASCAL CDynamicDoc::GetThisClass()
24
{
25 return ((CRuntimeClass*)(&CDynamicDoc::classCDynamicDoc));
26 }
27
28 CRuntimeClass* CDynamicDoc::GetRuntimeClass() const
29
{
30 return ((CRuntimeClass*)(&CDynamicDoc::classCDynamicDoc));
31 }如果你想看這些宏的簡(jiǎn)化版,可以參考侯老的《深入淺出MFC》,如下:
1
//in header file
2
class CView : public CWnd
3

{
4
public:
5
static CRuntimeClass classCView;
6
virtual CRuntimeClass* GetRuntimeClass() const;
7
//……
8
};
9
//in implementation file
10
static char_lpszCView = "CView";
11
CRuntimeClass CView::classCView =
12

{
13
_lpszCView
14
, sizeof(CView)
15
, 0xFFF
16
, NULL
17
, &CWnd::classCWnd
18
, NULL
19
};
20
static AFX_CLASSINIT _init_CView(&CView::classCView)
21

{
22
(&CView::classCView)->m_pNextClass = CRuntimeClass::pFirstClass;
23
CRuntimeClass::pFirstClass = &CView::classCView;
24
}
25
CRuntimeClass* CView::GetRuntimeClass() const
26

{
27
return &CView::classCView;
28
}
其中他將CRuntimeClass簡(jiǎn)化定義為:
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;
// CRuntimeClass objects linked together in simple list
static CRuntimeClass* pFirstClass; // start of class list
CRuntimeClass* m_pNextClass; // linked list of registered classes
};
//declaration file2
#define DECLARE_SERIAL(class_name) \3
_DECLARE_DYNCREATE(class_name) \4
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb);5

6
#define _DECLARE_DYNCREATE(class_name) \7
_DECLARE_DYNAMIC(class_name) \8
static CObject* PASCAL CreateObject();9

10
#define _DECLARE_DYNAMIC(class_name) \11
protected: \12
static CRuntimeClass* PASCAL _GetBaseClass(); \13
public: \14
static CRuntimeClass class##class_name; \15
static CRuntimeClass* PASCAL GetThisClass(); \16
virtual CRuntimeClass* GetRuntimeClass() const; \17
//implement file18
#define IMPLEMENT_SERIAL(class_name, base_class_name, wSchema) \19
CObject* PASCAL class_name::CreateObject() \20

{ return new class_name; } \21
extern AFX_CLASSINIT _init_##class_name; \22
_IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, \23
class_name::CreateObject, &_init_##class_name) \24
AFX_CLASSINIT _init_##class_name(RUNTIME_CLASS(class_name)); \25
CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) \26

{ pOb = (class_name*) ar.ReadObject(RUNTIME_CLASS(class_name)); \27
return ar; } \28
29
// generate static object constructor for class registration30
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);31
struct AFX_CLASSINIT32

{ AFX_CLASSINIT(CRuntimeClass* pNewClass)
{ AfxClassInit(pNewClass); } };33

34
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)35

{36
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();37
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);38
pModuleState->m_classList.AddHead(pNewClass);39
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);40
}41

42
43
#define _IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew, class_init) \44
CRuntimeClass* PASCAL class_name::_GetBaseClass() \45

{ return RUNTIME_CLASS(base_class_name); } \46

AFX_COMDAT CRuntimeClass class_name::class##class_name =
{ \47
#class_name, sizeof(class class_name), wSchema, pfnNew, \48
&class_name::_GetBaseClass, NULL, class_init }; \49
CRuntimeClass* PASCAL class_name::GetThisClass() \50

{ return _RUNTIME_CLASS(class_name); } \51
CRuntimeClass* class_name::GetRuntimeClass() const \52

{ return _RUNTIME_CLASS(class_name); } \53
54
#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))55

56
#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
//header file2
protected: 3
static CRuntimeClass* PASCAL _GetBaseClass(); 4
public: 5
static CRuntimeClass classCStroke; 6
static CRuntimeClass* PASCAL GetThisClass(); 7
virtual CRuntimeClass* GetRuntimeClass() const; 8
static CObject* PASCAL CreateObject();9
AFX_API friend CArchive& AFXAPI operator>>(CArchive& ar, CStroke* &pOb);10
//implement file11
//static12
static CObject* PASCAL CStroke::CreateObject()13

{14
return new CStroke;15
}16
//static17
static CRuntimeClass* PASCAL CStroke::GetThisClass();18

{ 19
return ((CRuntimeClass*)(&CStroke::classCStroke))20
}21
//static22
static CRuntimeClass* PASCAL CStroke::_GetBaseClass() 23

{ 24
return (CObject::GetThisClass());25
}26
//static27
static AFX_COMDAT CRuntimeClass CStroke::classCStroke = 28

{29
"CStroke"30
, sizeof(class CStroke)31
, 132
, CStroke::CreateObject33
, &class_name::_GetBaseClass34
, NULL35
, &_init_CStroke 36
}; 37
CRuntimeClass* CStroke::GetRuntimeClass() const38

{ 39
return ((CRuntimeClass*)(&CStroke::classCStroke)); 40
}41
extern struct AFX_CLASSINIT _init_CStroke;42
struct AFX_CLASSINIT _init_CStroke43

{44
void AFXAPI AfxClassInit(CRuntimeClass* CStroke)45

{46
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();47
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);48
pModuleState->m_classList.AddHead(CStroke);49
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);50
}51
};52
CArchive& AFXAPI operator>>(CArchive& ar, class_name* &pOb) 53

{ 54
pOb = (CStroke*) ar.ReadObject(RUNTIME_CLASS(CStroke));55
return ar; 56
}CStroke::classCStroke =
{
"CStroke"
, sizeof(class CStroke)
, 1
, CStroke::CreateObject
, &class_name::_GetBaseClass
, NULL
, &_init_CStroke
}
其中,由extern AFX_CLASSINIT _initCStroke可知_init_CStroke是一個(gè)結(jié)構(gòu)體AFX_CLASSINIT的對(duì)象,此結(jié)構(gòu)體有構(gòu)造函數(shù):
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);2
struct AFX_CLASSINIT3

{ AFX_CLASSINIT(CRuntimeClass* pNewClass)
{ AfxClassInit(pNewClass); } };4

5
void AFXAPI AfxClassInit(CRuntimeClass* pNewClass)6

{7
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();8
AfxLockGlobals(CRIT_RUNTIMECLASSLIST);9
pModuleState->m_classList.AddHead(pNewClass);10
AfxUnlockGlobals(CRIT_RUNTIMECLASSLIST);11
}

