青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

唐吉訶德

  C++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
  5 Posts :: 75 Stories :: 3 Comments :: 0 Trackbacks

常用鏈接

留言簿(2)

我參與的團(tuán)隊(duì)

搜索

  •  

最新評(píng)論

閱讀排行榜

評(píng)論排行榜

今天在網(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)生下列代碼:

1 BEGIN_MESSAGE_MAP(CCapturePacketDlg, CDialog)
2     ON_WM_PAINT()
3 END_MESSAGE_MAP()
  1. 先來(lái)分析ON_WM_PAINT(),在頭文件“afxmsg.h”有它的宏定義,如下:
1 #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)) }
,
說(shuō)明:層次序號(hào)x.y.z表示x為根節(jié)點(diǎn)也就是上面代碼中的行號(hào),y、z為上一級(jí)的定義展開(kāi)。
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):
1{
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 pointer
8        (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”中有定義:

 1#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):

 1__pragma(warning( push )) __pragma(warning( disable : 4867 ))
 2    const struct AFX_MSGMAP* CCapturePacketDlg::GetMessageMap() const
 3    
 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”中有定義:

1#define END_MESSAGE_MAP() \
2        {0000, AfxSig_end, (AFX_PMSG)0 } \
3    }; \
4        static const AFX_MSGMAP messageMap = \
5        &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
6        return &messageMap; \
7    }                                  \
8    PTM_WARNING_RESTORE

2.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.

·最后將

1BEGIN_MESSAGE_MAP(CCapturePacketDlg, CDialog)
2    ON_WM_PAINT()
3END_MESSAGE_MAP()
完全展開(kāi)為:
 1__pragma(warning( push )) __pragma(warning( disable : 4867 ))
 2    const struct AFX_MSGMAP* CCapturePacketDlg::GetMessageMap() const
 3    
 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 pointer
19                (AFX_MSG_CALL CCmdTarget::*)((AFX_MSG_CALL CWnd::*)(static_cast< void (AFX_MSG_CALL CWnd::*)(void>(&ThisClass :: OnPaint))
20            }
,
21            //add others
22            {
23                0,
24                0,
25                0,
26                0,
27                0,
28                (AFX_PMSG)0
29            }

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
其中GetMessageMap()是在哪里聲明的呢?在CCapturePacketDlg的定義中有一個(gè)這樣的宏:DECLARE_MESSAGE_MAP()
老辦法查看它的定義:
1#define DECLARE_MESSAGE_MAP() \
2protected: \
3    static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
4    virtual const AFX_MSGMAP* GetMessageMap() const; \
注意函數(shù)為static,即它們是類(lèi)的函數(shù)。函數(shù)中的static變量實(shí)際也在類(lèi)對(duì)象未生成之前已經(jīng)存在。(這種說(shuō)法不知道是否正確?)
小結(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):
1// not serializable, but dynamically constructable
2    #define DECLARE_DYNCREATE(class_name) \
3        DECLARE_DYNAMIC(class_name) \
4        static CObject* PASCAL CreateObject();
3.1如下定義:
1#ifdef _AFXDLL
2    #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; \
so the result(DECLARE_DYNCREATE(CDynamicDoc)) of combining the above two is following:
1protected
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):
1#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)
4.1如下定義:
 1#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); } \
4.1.2 CRuntimeClass如下定義:
 1struct CRuntimeClass
 2    {
 3    // Attributes
 4        LPCSTR m_lpszClassName;
 5        int m_nObjectSize;
 6        UINT m_wSchema; // schema number of the loaded class
 7        CObject* (PASCAL* m_pfnCreateObject)(); // NULL => abstract class
 8    #ifdef _AFXDLL
 9        CRuntimeClass* (PASCAL* m_pfnGetBaseClass)();
10    #else
11        CRuntimeClass* m_pBaseClass;
12    #endif
13
14    // Operations
15        CObject* CreateObject();
16        BOOL IsDerivedFrom(const CRuntimeClass* pBaseClass) const;
17
18        // dynamic name lookup and creation
19        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    // Implementation
25        void Store(CArchive& ar) const;
26        static CRuntimeClass* PASCAL Load(CArchive& ar, UINT* pwSchemaNum);
27
28        // CRuntimeClass objects linked together in simple list
29        CRuntimeClass* m_pNextClass;       // linked list of registered classes
30        const AFX_CLASSINIT* m_pClassInit;
31    }
;
4.1.2.30 AFX_CLASSINIT如下定義:(這個(gè)變量非常重要,它完成了將新的類(lèi)加在List頭部的功能,List中的節(jié)點(diǎn)類(lèi)型就是CRuntimeClass)
 1/////////////////////////////////////////////////////////////////////////////
 2    // Basic object model
 3
 4    // generate static object constructor for class registration
 5    void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);
 6    struct AFX_CLASSINIT
 7        { AFX_CLASSINIT(CRuntimeClass* pNewClass) { AfxClassInit(pNewClass); } };
 8    //C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\src\mfc\objcore.cpp Line157
 9    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如下定義:
1#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())
4.1.4 AFX_COMDAT如下定義:
1#define AFX_COMDAT __declspec(selectany)
說(shuō)明:“#”——operator (#) converts macro parameters to string literals without expanding the parameter definition.
“##”——operator (##), which is sometimes called the "merging" operator, is used in both object-like and function-like macros.
4.1.8 _RUNTIME_CLASS如下定義:
1#define _RUNTIME_CLASS(class_name) ((CRuntimeClass*)(&class_name::class##class_name))
so the result(IMPLEMENT_DYNCREATE(CDynamicDoc, CDocument)) of combining the aboves is following:
 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é):注意了,上面的成員變量、很多函數(shù)都是static
如果你想看這些宏的簡(jiǎn)化版,可以參考侯老的《深入淺出MFC》,如下:

 1//in header file
 2class CView : public CWnd
 3{
 4public:
 5    static CRuntimeClass classCView;
 6    virtual CRuntimeClass* GetRuntimeClass() const;
 7    //……
 8}
;
 9//in implementation file
10static char_lpszCView = "CView";
11CRuntimeClass CView::classCView =
12{
13    _lpszCView
14    , sizeof(CView)
15    , 0xFFF
16    , NULL
17    , &CWnd::classCWnd
18    , NULL
19}
;
20static AFX_CLASSINIT _init_CView(&CView::classCView)
21{
22    (&CView::classCView)->m_pNextClass = CRuntimeClass::pFirstClass;
23    CRuntimeClass::pFirstClass = &CView::classCView;
24}

25CRuntimeClass* 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
};

三、宏DECLARE_SERIAL(CStroke)、IMPLEMENT_SERIAL(CStroke, CObject, 1),給出它們的宏定義及結(jié)果:
 1//declaration file
 2#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 file
18#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 registration
30    void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);
31    struct AFX_CLASSINIT
32        { 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())

 1//header file
 2    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 file
11    //static
12    static CObject* PASCAL CStroke::CreateObject()
13    {
14        return new CStroke;
15    }

16    //static
17    static CRuntimeClass* PASCAL CStroke::GetThisClass();
18    
19        return ((CRuntimeClass*)(&CStroke::classCStroke))
20    }

21    //static
22    static CRuntimeClass* PASCAL CStroke::_GetBaseClass() 
23    
24        return (CObject::GetThisClass());
25    }

26    //static
27    static AFX_COMDAT CRuntimeClass CStroke::classCStroke = 
28    {
29        "CStroke"
30        , sizeof(class CStroke)
31        , 1
32        , CStroke::CreateObject
33        , &class_name::_GetBaseClass
34        , NULL
35        , &_init_CStroke 
36    }

37    CRuntimeClass* CStroke::GetRuntimeClass() const
38    
39        return ((CRuntimeClass*)(&CStroke::classCStroke)); 
40    }

41    extern struct AFX_CLASSINIT _init_CStroke;
42    struct AFX_CLASSINIT _init_CStroke
43    {
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    }
總結(jié),一旦RUNTIME_CLASS(CStroke)由#define RUNTIME_CLASS(class_name) (class_name::GetThisClass())也就是CStroke::GetThisClass() 即
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ù):
 1void AFXAPI AfxClassInit(CRuntimeClass* pNewClass);
 2    struct AFX_CLASSINIT
 3        { 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    }
所以一旦返回classCStroke,也就調(diào)用了_init_CStroke的構(gòu)造函數(shù)即將類(lèi)CStroke添加到了全局變量m_classList類(lèi)的List中了,同時(shí)在變量classCStroke中,也可以得到類(lèi)CStroke的名稱、大小、一個(gè)CStroke的對(duì)象、類(lèi)CStroke的基類(lèi)以及AFX_CLASSINIT結(jié)構(gòu)的一個(gè)對(duì)象。
posted on 2010-10-26 12:00 心羽 閱讀(1585) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): 需求分析
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美日韩色婷婷| 亚洲精品国产视频| 欧美激情在线播放| 欧美日韩国产小视频| 一区二区三区国产精华| 男人天堂欧美日韩| 久久中文精品| 91久久极品少妇xxxxⅹ软件| 欧美高清免费| 午夜亚洲福利在线老司机| 久久精品日韩一区二区三区| 国产精品久久久久高潮| 欧美一级久久| 亚洲日本视频| 国产精品国产a级| 欧美一区观看| 久久国产精品久久久久久电车| 久久精品国产久精国产一老狼 | 久久一区二区视频| 国产精品xxxxx| 在线亚洲国产精品网站| 久久天天综合| 亚洲一区二区三区国产| 蜜桃久久av| 在线日韩成人| 亚洲综合视频1区| 欧美+亚洲+精品+三区| 国产精品丝袜久久久久久app| 久久久久久色| 久久gogo国模裸体人体| 精品成人国产在线观看男人呻吟| 狠狠色丁香久久婷婷综合丁香 | 久久综合九色综合欧美就去吻| 欧美高清在线视频观看不卡| 看片网站欧美日韩| 国产一区二三区| 久久精品30| 日韩网站在线| 麻豆精品网站| 午夜免费在线观看精品视频| 欧美图区在线视频| 欧美69视频| 一区二区在线视频观看| 亚洲欧洲精品一区二区三区不卡 | 国产欧美在线播放| 欧美成人午夜77777| 久久亚洲精品中文字幕冲田杏梨| 久久久精品五月天| 美国十次了思思久久精品导航| 美脚丝袜一区二区三区在线观看| 欧美日韩国内| 永久免费毛片在线播放不卡| 亚洲精品国产系列| 午夜精品福利电影| 一区二区三区日韩欧美精品| 久久久国产精彩视频美女艺术照福利| 欧美a级在线| 欧美视频在线一区二区三区| 欧美成人综合一区| 久久综合中文色婷婷| 亚洲国产免费看| 欧美成年人网| 韩国女主播一区| 久久久av水蜜桃| 亚洲欧美第一页| 欧美片第1页综合| 亚洲精品在线二区| 免费成人高清在线视频| 亚洲一本大道在线| 欧美激情综合五月色丁香| 亚洲成色最大综合在线| 亚洲人成高清| 久久午夜电影网| 国产精品日本精品| 亚洲淫性视频| 亚洲欧美影院| 在线精品一区| 日韩视频专区| 国产精品视频一| 久久精品主播| 久久人人97超碰人人澡爱香蕉| 国产伦精品一区二区三区高清版 | 国内久久婷婷综合| 久久精品国产2020观看福利| 久久福利资源站| 亚洲高清不卡av| 一本色道**综合亚洲精品蜜桃冫| 国产欧美日韩精品a在线观看| 久久久久久一区| 欧美精品一区二区三区一线天视频| 亚洲一区二区三| 欧美精品在线观看播放| 国产欧美日韩一区二区三区在线观看 | 国产精品日本精品| 亚洲一区二区免费看| 欧美~级网站不卡| 久色婷婷小香蕉久久| 尤物99国产成人精品视频| 久久精品国产一区二区电影| 久久久久久午夜| 亚洲黄色一区二区三区| 欧美日韩免费观看一区| 亚洲综合不卡| 亚洲精品国产精品国自产观看浪潮| 亚洲国产一区二区三区高清| 欧美日本免费| 久久只有精品| 亚洲美女视频在线观看| 噜噜噜噜噜久久久久久91| 99视频精品| 最新中文字幕亚洲| 国产一区91| 国产精品v欧美精品v日韩 | 亚洲成人在线免费| 欧美电影在线播放| 亚洲免费在线观看视频| 久热精品视频在线观看| 羞羞视频在线观看欧美| 亚洲美女免费精品视频在线观看| 99国产精品久久久久久久| 欧美一级二级三级蜜桃| 亚洲精品一区在线观看| 激情欧美一区| 国产日韩在线一区| 国产美女精品一区二区三区| 国产精品日产欧美久久久久| 欧美午夜一区二区福利视频| 欧美日韩免费高清| 国产精品久久久久久久午夜片| 久久综合电影一区| 久久久xxx| 欧美激情偷拍| 国产乱肥老妇国产一区二 | 欧美一级片一区| 欧美伊人久久| 欧美精品成人一区二区在线观看| 欧美黄色aaaa| 国产乱码精品一区二区三区五月婷 | 国产精品一区=区| 一区二区三区无毛| 亚洲午夜小视频| 噜噜噜久久亚洲精品国产品小说| 亚洲福利在线看| 亚洲在线观看视频网站| 蜜桃久久av| 亚洲二区在线视频| 久久成人一区二区| 一区二区三区视频免费在线观看| 午夜视黄欧洲亚洲| 欧美国产日韩精品| 在线观看视频亚洲| 久久久久久999| 一区二区三区视频观看| 亚洲女同在线| 国产精品一区二区你懂得| 亚洲另类一区二区| 开元免费观看欧美电视剧网站| 亚洲香蕉网站| 国产精品午夜在线| 一本久道综合久久精品| 亚洲美女在线看| 欧美精品综合| 99re66热这里只有精品3直播 | 亚洲一区高清| 中文亚洲字幕| 国产日韩欧美在线播放| 久久不射中文字幕| 亚洲欧美在线高清| 国产日韩欧美高清免费| 久久成人亚洲| 久久美女性网| 一道本一区二区| 欧美在线3区| 夜夜爽av福利精品导航| 一本色道久久综合亚洲精品不卡 | 亚洲免费观看高清在线观看| 欧美日产在线观看| 亚洲欧美第一页| 欧美在线在线| 中国女人久久久| 久久精品视频在线免费观看| 日韩视频中午一区| 性做久久久久久| 亚洲一区二区三区高清不卡| 久久国产主播精品| 午夜欧美精品| 欧美久久影院| 亚洲国产va精品久久久不卡综合| 国产精品美女久久久久久2018| 亚洲免费在线视频| 亚洲一区二区三区视频| 久久免费一区| 久久电影一区| 欧美一区深夜视频| 亚洲免费精彩视频| 免费短视频成人日韩| 媚黑女一区二区| 国内精品久久久久国产盗摄免费观看完整版| 亚洲国产精品一区制服丝袜| 狠狠久久五月精品中文字幕|