参考书籍:
《Effective C++ 》
《C++ Primer》
《C++ 编程思想》上下册
算来已经好久没有写博客了,周末抽点时间记录下最近学习的一个小问题吧。
一直在使用C++写个小实验,以前没有写过特别大的工程,也很少会考虑到使用和不使用#define这个宏的好处和有点,最多也就是知道它是在预处理时期“盲目替换”代码的,因此不会牵涉到函数调用也就更不会有函数调用带来的堆栈开销。今天稍微详细了解了一下,所以几记录下来:
首先,简单罗列一下#define的优点和好处:#define不分配内存,它是预编译命令,编译之前进行宏替换;使用带参数的#define可以完成类似于函数调用的功能,而不用付出函数调用的代价(函数调用前需要保留函数的现场,函数返回时需要恢复现场等)。不过,我这里要说的重点不是#define带来的有点,而是它给我们带来的麻烦以及我们应该怎么样去解决。
- #define没有类型,不能够进行类型检查;
- #define的宏不进入记号表(symbol table),因此程序出错时很难发现;
- #define只是盲目地替代,因此会出现不可预知的错误,且不易被发现;
- #define没有作用域(scope)的概念,一旦定于就会在随后的编译过程中有效;
#define PI 3.14159
const double Pi = 3.14159
这样,编译器就能够看到,当然也会进入符号表。在编译过程总出现错误的话编译器就会提示Pi变量出错,而不再是3.14159这个magic number,你也就不用花费更多的精力去追踪错误的罪魁祸首是谁了。当然了,编译器也能够进行数据类型的检查,因为Pi有自己的数据类型。
class A { public : static const double Pi ; };
当然了,这里只是声明了一个作用于在A中的静态常量,还需要对它进行定义
const double A :: Pi = 3.14159 ;
这样,就建立了一个class A专属的常量,这一点#define是做不到的。同样,#define更不能够进行封装,也就是说,Pi可以被定义为A的私有常量,也可以定义为其protected常量,而#define是不能的。
class A { public : enum { Num = 100 }; };
OK , 你不能取Num的地址或者reference了吧!!不过还是有问题的 .... 那就是这仅仅适用于整形数的情况 ??? 两外的情况该怎么办啊 ?? 这个我想以后在说吧(后面的博客会详细说明)。
#include <iostream> #define MAX(a, b) ( a ) > ( b ) ? ( a ) : ( b ) using namespace std; int main (int argc, char **argv) { int n1 = 3, n2 = 2; int result = MAX(++ n1, n2); cout << result << endl; return 0; }
我们的本意是得到++n1 ( = 4)和 n2(= 2)中的较大的至,显然这个值应该是4,可你觉得我们会得到正确的结果吗???先问问VS吧 ......
#include <iostream> using namespace std; template <typename T> inline T& Max( T& a, T& b) { return a > b ? a : b; } int main (int argc, char **argv) { int n1 = 3, n2 = 2; int result = Max(++ n1, n2); cout << result << endl; return 0; }
这个结果是什么呢?还是先问问VS吧: