VC新手,在用MFC编程的时候经常会看代码中有很多宏的定义,我对宏这个东西很迷糊,请各位高手来说说你们对宏的理解,也会对我有很大的帮助的!
解决方案 »
- 在程序中不断的new和delete会不会造成内存碎片,导致系统异常或变慢?
- 【求助】指针类型转换问题
- mfc 如何不弹出选择连接数据源的对话框
- 怎样实现像windows里附件“画图”点击新建与再次点击新建之间的处理。
- 如何动态生成XML文档和解析XML文档?
- 高手请进,急急急急急急急急急急急急急急急急急急!!!!!!!!!!!!!!
- 有没有和ATL,COM有关的,比较适合菜鸟学习的网站呢?
- 吧如果对VC很熟悉的话 学用ATL编写COM组件 只有花上一天时间 就可以上手了,真的吗?
- 如何做一个类似于开始菜单中的文档一类的东西?
- 如何禁止它显示滚动条?
- 如何判断本程序是被其它程序启动的还是被人直接启动的?
- [help] 是否可以自定义VS 2005 项目属性的宏
http://www.vckbase.com/document/viewdoc/?id=957
对宏扩展的预处理在所有那些不是预处理指令的行(第一个非空白字符不是#的行),以及其指令并未作为条件编译的一部分而忽略的行中进行。“条件编译”指令允许通过检测一个常量表达式或标识符以决定在预处理过程中哪个文本块送入编译器、哪个文本块从源文件中删除,并以此种方式控制一个源文件中某部分的编译。#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
在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);
}可是以上内容中还是有不认识的宏,那就继续搜吧,直到把所有的宏都展开,心中有数后就明白了.
以上只是我自己常用的方法,仅供参考.