在写程序时经常会碰到这样一个问题,我们需要重复写很多相同的代码,并且这些代码结构相同。总是想自己把这段代码封装一下然后直接进行调用,但是如果这段代码逻辑并不复杂,并且代码量也不大,不适合进行封装,那么我们就会想到c++中的关键字define。其实明智的你,遇到上面的这些情况估计还会想到另一个东西----template,后面我们也会说一下两者的却别。今天自己看了一下c++ define的相关知识,做个记录。
1.什么是define: 宏定义,简单的理解就是替换,其实这也是本质。如果熟悉g++编译过程的话,会了解到一个概念叫做预处理,就是在编译之前做个处理。这个过程并不像编译那么复杂,就是简单的递归替换和删除。替换的就是宏定义和include文件,删除注释。注意这里我们谈到一个概念,递归替换,这个其实是很常见的,比如你的程序中include一个.h文件,但是这个.h文件中还引入了另一个.h文件,那么这个时候就需要进行递归替换。这个我们自己查看一下究竟预处理都干了些什么?(为了减少预处理之后的代码我们不引入其他文件)
这样我们就更好的理解define的作用和预处理的作用了。
2.define的使用方法:
-
- 简单使用:define identifier replacement-list(optional) 这是最基本的使用方法,就像上面我们的例子一下,在预处理的时候将identifier替换成replacement-list(optional)。比如:在开发程序的时候会定义很多错误码,比如使用22表示图片上传成功,如果我们这样进行判断assert(result==22)。看似是没什么问题,那么要是你看了谁的代码是这样写的,你会怎么想?谁写的,这22是什么意思? 这时候就需要我们给22起一个别名,例如: #define Success = 22
- 带参形式:除了上面的使用方法我们还可以给定参数来进行宏定义,比如:
#include<iostream> #include<string.h> //定义了一个宏,将A,B进行连接 #define Append(A,B) A+B; using namespace std; int main(int argc, char *argv[]) { string test1 = "1"; string test2 = "2"; //进行宏定义的调用 cout<<Append(test1,test2); return 0; }
从这个例子我们可以看出宏定义的好处之一:可以在一定程度上忽略宏定义中参数的类型,这一点是不是和template很相似,我们使用template来重写一下上面的这段代码:
#include<iostream> #include<string.h> using namespace std; //定义模板方法 template<class T> T append(T a,T b){ return a+b; } int main(int argc, char *argv[]) { string A = "1"; string B = "2"; //使用模板方法 cout<<append(A,B); return 0; }
-
这里我们是不是会觉得在这种情况下面宏定义与template是一致的?其实并不是,我们考虑一下下面的这种情况,假设存在这样一个宏定义#define add(a,b) a+b,并且在代码中调用过程是这样的a*add(a,b)*b;大家可以想想结果是多少?答案是5,而不是我们预期的6,原因很简单,这只是简单的替换,经过预处理之后的结果是a*a+b*b。但是template运算的结果就是6,这就是差别。所以在使用的时候还是应该考虑是选择template还是define。
-
define中代码段的表示: 上面我们说到的宏定义都是简单的一条语句,如果我们需要定义一个稍微复杂一点的语句呢?比如:我们习惯,或者说是个人习惯,喜欢把一些判断语句进行宏定义,比如我们在进行一个功能错误码的校验的时候通常希望能够将代码和log日志分开,使得代码显得不那么凌乱,那么我们将会定义一个稍微复杂一点的宏定义。
#include<iostream> #include<string.h> //宏定义一个MaxName #define MaxName 10 //宏定义 定义了一个判断条件用于识别图片上传之后的返回码 并输出日志条件 #define AssertReturnValue(name,result)do{\ if(result==0)\ cout<<"图片"<<name<<"上传成功";\ }while(0) using namespace std; class Picture { public: Picture (const char *path,const char *file){ strcpy(this->path,path); strcpy(this->file,file); } ~Picture (){ } private: char file[MaxName]; char path[MaxName]; }; int uploadPicture(const Picture picture) { cout<<"成功"; return 0; } int main(int argc, char *argv[]) { Picture picture("100","100"); int result = uploadPicture(picture); //调用结果判断宏定义 AssertReturnValue("100",result); return 0; } 这里我们应该注意一下,代码段的宏定义的写法。 条件宏定义ifdef:可以在编译的时候通过#define设置编译环境,语法结构
//不同的运行环境,不同的头文件
#ifdef OS_Win
#include <windows.h>
#endif
#ifdef OS_Linux
#include <linux.h>
#endif
下面我们将c++ 常用的宏定义罗列一下:
#空指令,无任何效果
#include包含一个源代码文件
#define定义宏
#undef取消已定义的宏
#if如果给定条件为真,则编译下面代码
#ifdef如果宏已经定义,则编译下面代码
#ifndef如果宏没有定义,则编译下面代码
#elif如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif结束一个#if……#else条件编译块
#error停止编译并显示错误信息