1.为什么c++中不提倡使用宏呢?
这主要是由于宏背后所潜藏的安全机制以及误用频繁造成的。
对于宏,我们应明白其实质就是“替换”,编译器在编译阶段就会进行宏替换,使用你所定义的常量(函数)替换所有出现该定义位置,其替换过程说白了,仅仅是字符替换而已,因而会因此发生很多误用,最常见的就是所谓的“边际效应”。
例如:
#define N 100
#define M 200 + N
当你在程序中使用 M*N 时,原本想要的应该是100 * (200+ N ),而实际上,编译器却给你变成了 100 * 200 + N,这就是所谓的边际效应,说白了,就是替换出现了错误。(我在后面会讲述这一问题的注意事项)
形成良好地编写习惯是一个优秀程序员的基本素质。记住一句话:编程是一门艺术,态度决定高度,细节决定成败!
而且,宏定义,缺乏类型定义,因而也就自然没有类型安全性检测。这和const形成鲜明的对比,const定义的常量编译器可以对其进行数据静态类型安全检查(比如,int型,double型等),而#define宏定义的常量却只是进行简单的字符替换,你不要指望编译告诉你,定义的数值过大等等。
还有,宏定义无法进行调试。由于在编译前的预编译阶段已经进行了宏替换,所以在更后面的调试等等环节,你根本不用指望调试中出现你所定义的原型,有时候,这样就造成很多困扰。再此,我建议也可以阅读以下《effective c++》的相关章节,链接如下:
http://www.kuqin.com/effectivec2e/ch00b.htm
2.为什么c++中又允许宏定义?
首先,肯定是为了与c兼容。
其次,虽然宏定义有种种弊端,但这些实际上是c所带来的避短而已,我们通过刻意训练完全可以避免,而且,说实话,宏定义确实带来了某些好处,后面的文章里会讲的很清楚。
再其次,有时候,宏定义是你唯一的,或者最便捷的选择(在保证安全性前提下)。比如,在MFC下常见的声明与注销(#undef),条件编译(我更喜欢把条件编译称为一个开关,具体我会在后面阐述)等。
3.c++中宏定义的使用方法进阶
阅读本文章的你,我会认为你已经掌握了基本的宏定义使用方法,包括:宏常量定义、无参数宏函数定义、有参数宏函数定义。本文仅就其衍生以及进行阐述。推荐先阅读下面链接:
http://www.cppblog.com/kevinlynx/archive/2008/03/19/44828.html
http://blog.163.com/lorry_717/blog/static/170530942201122211482485/
补充部分:
有参数宏函数定义中,"\","#","#@"和"##"说明:用#define 定义时 , 斜杠("\")是用来续行的,用于多行宏定义函数书写。"#"用来把参数转换成字符串,是给参数加上双引号"#@"是给参数加上单引号。"##"则用来连接前后两个参数,把它们变成一个字符串。我很有必要提醒一下你,这几种都是用于宏函数定义的,因此如果误用于常量定义,就会报“无效字符 : 可能是宏扩展的结果”等错误。
typedef与#define都可以用于定义,作用都类似于起别名。为了防止潜在的错误,我建议,在定义关键字、类型时,使用typedef,而定义常量、函数等用define。可以参考下面链接:
http://blog.163.com/xiangzaihui@126/blog/static/1669557492011822113133948/
常用的宏定义,在windef.h中已经给出定义,使用时只需包括该头文件或者直接包括Windows.h即可。里面已经给出的宏定义包括:max()、min()、MAKEWORD(),MAKELONG(),LOWORD(),HIWORD(),LOBYTE(),HIBYTE()以及其他常量定义。
4.c++中如何更好地使用宏定义?
也就是说:
1.求值宏要用括号括起来,避免产生二意。
2.对于带参数的宏,在宏体中引用宏参数时,最好也把参数用括号括起来,否则会产生歧义。
3.避免多次求值,应定义一个本地变量暂存所有会被引用多次的宏参数。如max()宏的安全定义为:
4.对于非求值的宏,即代码块宏,用do {....}while(0)包围代码块,防止产生意外代码。