VC新手,在用MFC编程的时候经常会看代码中有很多宏的定义,我对宏这个东西很迷糊,请各位高手来说说你们对宏的理解,也会对我有很大的帮助的!

解决方案 »

  1.   

    楼主可以看看这篇文章,写得较详细:
    http://www.vckbase.com/document/viewdoc/?id=957
      

  2.   


        对宏扩展的预处理在所有那些不是预处理指令的行(第一个非空白字符不是#的行),以及其指令并未作为条件编译的一部分而忽略的行中进行。“条件编译”指令允许通过检测一个常量表达式或标识符以决定在预处理过程中哪个文本块送入编译器、哪个文本块从源文件中删除,并以此种方式控制一个源文件中某部分的编译。#define指令通常使用有意义的标识符与常量、关键字、常用语句和表达式关联。表示常量的标识符有时被称作“符号常量”或“显式”常量。表示语句或表达式的常量称为“宏”。在本预处理器文档中,只使用术语“宏”。当宏的名称在程序源文本或在某些其它预处理器命令的参量中被识别时,它被处理为对该宏的调用。宏名称被宏体的一个拷贝所替换。若该宏接受参量,宏名称后的实参就会替换宏体中的形参。用宏体中处理的拷贝来替换一个宏调用的过程,称为宏调用的“扩展”。实际的术语中有两种类型的宏。“类对象”宏不带参量,而“类函数”宏可定义为带参量。因此它们的形式和功能都象函数调用,由于宏不生成实际的函数调用,所以有时可用宏替代函数调用使程序运行得更快,(在C++中,inline函数通常是一个好方法),然而,如果不小心的定义和使用宏,也可能造成麻烦。在带参量的宏定义时,你必须使用括号以保持一个表达式中正常的优先级,同时宏也不能正确地处理具有副作用的表达式。有关更多的信息参见“#define指令”中的例子getrandom。一旦你定义了一个宏,你不能不经取消该宏原有定义,而重新定义它为一个不同的值。但可用正好相同的定义来重定义该宏,因此,一个程序中宏的相同定义可出现多次。#undef指令用于取消宏的定义。一旦取消该宏的定义,就可重新定义该宏为一个不同的值。#define和#undef两节分别详细讨论了#define和#undef指令。————————————————————————————————————————————————
    5.1、宏和C++
         C++提供了一些新的功能。其中有些功能替代了原来由ANSI C所提供的功能。
         这些新的功能增强了类型安全性和该语言的可预测性:
             1) 在C++中,以const说明的对象可用于常量表达式中,这使程序可以说明有类型和值信息的常量,
                以及能被调试器逐个字符检查的枚举值的常量。使用预处理器指令#define定义常量并不精确。
                除非在程序中找到一个带地址的表达式,否则一个const对象将不分配任何存储。
             2) C++联编函数替代了函数类型宏,相对于宏来说使用联编函数的优势在于:
             3) 类型安全性。
                联编函数和一般函数一样需进行相同的类型检测,宏无类型安全性检测。
             4) 纠正具有副作用的参量处理。
                联编函数在进入函数体之前对参量的表达式求值。因此,一个有副作用的表达式将是安全的。
         对于联编函数的更多信息参见inline、_ _inline节。
         为了向下兼容,Microsoft C++保留了所有在ANSI C和更早C++规格中的预处理器功能。————————————————————————————————————————————————
    5.2、预定义宏
         编译器可识别六种预定义的ANSI C宏(参见表1.1),而Microsoft C++实现提供更多的预定义宏(参见表1.2)。
         这些宏不带参量,但不能被重定义。它们的值(除__LINE_ _和_ _FILE_ _外)必须是经过编译的常量。
         下面列出的一些预定义宏须用多个值来定义,
         它们的值可在Visual C++开发环境中选择相应的菜单选项来设置或采用命令行开关。更多的信息参见下表。     表1.1  ANSI 预定义宏
              宏              说明
              __DATE__        当前源文件的编译日期。日期是格式为Mmm dd yyyy的字符串文字。
                              月份名称Mmm与在TIME.H中说明的库函数asctime产生的日期一样
              __FILE__        当前源文件名称。__FILE_ _扩展为用双引号括起的一个字符串
              __LINE__        当前源文件的行号。该行号是一个十进制整型常量。可用一个#line指令修改
              __STDC__        指出与ANSI C标准的完全一致性。
                              仅当给出/Za编译器选项且不编译C++代码时定义为整型量1;否则是不确定的
              __TIME__        当前文件的最近编译时间。该时间是格式为hh:mm:ss的字符串文字
              __TIMESTAMP__   当前源文件的最近修改日期。日期是格式为Ddd Mmm Datehh:mm:ss yyyy的字符串文字,
                              这里Ddd是星期几的简写,Date是从1到31的一个整数     表1.2  Microsoft特殊预定义的宏
              宏                说明
              __CHAR_UNSIGNED   缺省char类型是无符号的,当指定/J时定义的
              __cplusplus       仅为C++程序定义
              __CPPRTTI         定义为用/GR编译的代码(允许运行时类型信息)
              __CPPUNWIND       定义为用/GX编译的代码(允许异常处理)
              __DLL             指定/MD或/MDd(多线程DLL)时定义的
              __M_ALPHA         为DEC ALPHA平台定义,使用ALPHA编译器时定义为1,
                                若使用另一个编译器时不定义
              __M_IX86          为x86处理器定义,参见表1.3
              __M_MPPC          为Power Macintosh平台定义,缺省为601(/QP601)参见表1.4
              __M_MRX000        为MIPS平台定义,缺省为4000(/QMR4000),参见表1.5
              __M_PPC           为PowerPC平台定义,缺省为604(/QP604),参见表1.6
              __MFC_VER         为MFC版本定义,为Microsoft Founndation类库4.21定义为0x0421,它总是定义的
              __MSC_EXTENSIONS  该宏在使用/Ze编译选项(缺省值)时定义,定义时其值总为1
              __MSC_VER         定义编译器版本,对于Microsoft Visual C++ 6.0定义为1200,它总是定义的
              __MT              当指定/MD或/MDd(多线程DLL)或/MT或/MTd(多线程)选项时定义
              __WIN32           为Win32应用程序而定义。它总是定义的     如下表所示,编译器对反映处理器选项的预处理器标识符产生一个值。
         表1.3  _M_IX86的值
              开发者的选项     命令行选项   返回值
              Blend            /GB          _M_IX86=500(缺省值。将来的编译器将给出一个不同的值以影响主处理器)
              Pentium          /G5          _M_IX86=500
              Pentium Pro      /G6          _M_IX86=600
              80386            /G3          _M_IX86=300
              80486            /G4          _M_IX86=400     表1.4  _M_MPPC的值
              开发者的选项     命令行选项   返回值
              PowerPC 601      /QP601        _M_MPPC=601(缺省值)
              PowerPC 603      /QP603        _M_MPPC=603
              PowerPC 604      /QP604        _M_MPPC=604
              PowerPC 620      /QP620        _M_MPPC=620     表1.5 _M_MRX000的值
              开发者选项       命令行选项    返回值
              R4000            /QMR4000      _M_MRX000=4000(缺省值)
              R4100            /QMR4100      _M_MRX000=4100
              R4200            /QMR4200      _M_MRX000=4200
              R4400            /QMR4400      _M_MRX000=4400
              R4600            /QMR4600      _M_MRX000=4600
              R10000           /QMR10000     _M_MRX000=10000     表1.6  _M_PPC的值
              开发者选项       命令行选项    返回值
              PowerPC 601      /QP601        _M_PPC=601
              PowerPC 603      /QP603        _M_PPC=603
              PowerPC 604      /QP604        _M_PPC=604(缺省值)
              PowerPC 620      /QP620        _M_PPC=620
      

  3.   

    在VC安装目录\Microsoft Visual Studio\VC98\MFC下的所有文件中搜索这个宏,如IMPLEMENT_DYNAMIC
    在AFX.H第827行:
    #define IMPLEMENT_DYNAMIC(class_name, base_class_name) \
    IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, 0xFFFF, NULL)
    其中"\"是个行连接符,表示把接下来一行连接到第一行的后面.
    但是这个宏中还用到了另一个宏IMPLEMENT_RUNTIMECLASS,那就继续搜IMPLEMENT_RUNTIMECLASS,在AFX.h第811行:
    #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
        AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name = { \
            #class_name, sizeof(class class_name), wSchema, pfnNew, \
                RUNTIME_CLASS(base_class_name), NULL }; \
            CRuntimeClass* class_name::GetRuntimeClass() const \
            { return RUNTIME_CLASS(class_name); } \够乱的吧,需要我们自己整理一下(宏只能在一行中定义,行连接符\不能去掉,但为了清晰,这里将\去掉):
    #define IMPLEMENT_RUNTIMECLASS(class_name, base_class_name, wSchema, pfnNew) \
        AFX_COMDAT const AFX_DATADEF CRuntimeClass class_name::class##class_name =
        {
            #class_name, //#是"字符串化运算符",将class_name代表的内容变成字符串.
            sizeof(class class_name),       //对象大小.
            wSchema,                        //模式号,0xFFFF = -1.
            pfnNew,                         //指向创建对象的默认构造函数,NULL表示什么也不做.
            RUNTIME_CLASS(base_class_name), //得到base_class_name中的CRuntimeClass对象地址.
            NULL                            //最后一个数据成员CRuntimeClass* m_pNextClass;
        };
        //以上是对DECLARE_DYNAMIC宏定义的CRuntimeClass成员对象进行初始化.注意第5个成员,
        //被初始化为RUNTIME_CLASS(base_class_name),即指向基类中的CRuntimeClass结构的指针.
        CRuntimeClass* class_name::GetRuntimeClass() const
        {
            return RUNTIME_CLASS(class_name);
        }可是以上内容中还是有不认识的宏,那就继续搜吧,直到把所有的宏都展开,心中有数后就明白了.
    以上只是我自己常用的方法,仅供参考.