赞
踩
宏定义,简单的理解就是替换,其实这也是本质。如果熟悉g++编译过程的话,会了解到一个概念叫做预处理,就是在编译之前做个处理。这个过程并不像编译那么复杂,就是简单的递归替换和删除。替换的就是宏定义和include文件,删除注释。注意这里我们谈到一个概念,递归替换,这个其实是很常见的,比如你的程序中include一个.h文件,但是这个.h文件中还引入了另一个.h文件,那么这个时候就需要进行递归替换。
#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,原因很简单,这只是简单的替换,经过预处理之后的结果是aa+bb*。但是template运算的结果就是6,这就是差别。所以在使用的时候还是应该考虑是选择template还是define。
#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 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 停止编译并显示错误信息
参考http://www.cnblogs.com/zi-xing/p/4550246.html
#define MAXTIME 1000
一个简单的MAXTIME就定义好了,它代表1000,如果在程序里面写
if(i<MAXTIME){.........}
编译器在处理这个代码之前会对MAXTIME进行处理替换为1000。
这样的定义看起来类似于普通的常量定义CONST,但也有着不同,因为define的定义更像是简单的文本替换,而不是作为一个量来使用,这个问题在下面反映的尤为突出。
define可以像函数那样接受一些参数,如下
#define max(x,y) (x)>(y)?(x):(y);
这个定义就将返回两个数中较大的那个,看到了吗?因为这个“函数”没有类型检查,就好像一个函数模板似的,当然,它绝对没有模板那么安全就是了。可以作为一个简单的模板来使用而已。
但是这样做的话存在隐患,
#define Add(a,b) a+b;
在一般使用的时候是没有问题的,但是如果遇到如:c * Add(a,b) * d的时候就会出现问题,代数式的本意是a+b然后去和c,d相乘,但是因为使用了define(它只是一个简单的替换),所以式子实际上变成了
ca + bd
#define pin (int*);
pin a,b;
本意是a和b都是int型指针,但是实际上变成int* a,b;
a是int型指针,而b是int型变量。
这是应该使用typedef来代替define,这样a和b就都是int型指针了。
所以我们在定义的时候,养成一个良好的习惯,
建议所有的层次都要加括号。
#define Conn(x,y) x##y 表示连接字符串
#define ToChar(x) #@x 表示加上单引号
#define ToString(x) #x 表示加上双引号
我们假设:x=1,则有:
Conn(1)------〉T_1
ToChar(1)------〉‘1’
ToString(1)------〉“1”
define可以替代多行的代码,例如MFC中的宏定义(非常的经典,虽然让人看了恶心)
#define MACRO(arg1, arg2) do { /
/* declarations */ /
stmt1; /
stmt2; /
/* ... */ /
} while(0) /* (no trailing ; ) */
关键是要在每一个换行的时候加上一个"/"
4.在大规模的开发过程中,特别是跨平台和系统的软件里,define最重要的功能是条件编译。
就是:
#ifdef WINDOWS
......
......
#endif
#ifdef LINUX
......
......
#endif
可以在编译的时候通过#define设置编译环境
#ifdef XXX…(#else) … #endif
例如
#ifdef DV22_AUX_INPUT
#define AUX_MODE 3
#else
#define AUY_MODE 3
#endif
#ifndef XXX … (#else) … #endif
//定义宏
#define [MacroName] [MacroValue]
//取消宏
#undef [MacroName]
//普通宏
#define PI (3.1415926)
带参数的宏
#define max(a,b) ((a)>(b)? (a),(b))
关键是十分容易产生错误,包括机器和人理解上的差异等等。
重复包含(重复定义)
由于头文件包含可以嵌套,那么C文件就有可能包含多次同一个头文件 或 两个头文件循环调用 就可能出现重复定义的问题。
- 在c语言中,对同一个变量或者函数进行多次声明是不会报错的。所以如果h文件里只是进行了声明工作,即使不使用# ifndef宏定义,一个c文件多次包含同一个h文件也不会报错。
- 但如果你在头文件A里定义了结构体或者类类型(这是最常见的情况),在h文件中定义了全局变量,一个c文件包含同一个h文件多次,如果不加#ifndef宏定义,会出现变量重复定义的错误;如果加了#ifndef,则不会出现这种错.
通过条件编译开关来避免重复包含(重复定义)
可参考 链接
例如
#ifndef __headerfileXXX__
#define __headerfileXXX__
…
//文件内容
…
#endif
以上只是我从网络上搜集了一些关于define的一些用法,可能还不全面。
在c++中,
- 定义常变量,应该用const定义,
- 定义常用函数,应该用 inline 定义,
这两种方法更好。
只是很初级的一种代换,实现的功能很单一
而且安全性很差,比如类型错误、括号漏写 都会造成很大的错误,
而且错误不容易被发现,隐患很大
内联函数要比前者好很多 功能也要全面很多!
最主要的是 内联函数能够进行安全检查(比如参数类型 等)
如果在能够使用两着的情况之下 推荐使用 内联不过有两点要注意:
- 1
- 内联 是以代码膨胀为代价的,
不是所有的函数都适合用 内联 方式
要考虑函数的实际情况- macro定义 也不是说一无是处了
在合适的时候使用 也许会有意想不到的效果
最大的差别:
- 前者在堆栈分配了空间,而后者只是把具体数值直接传递到目标变量罢了。
- 或者说,const的常量是一个Run-Time的概念,他在程序中确确实实的存在可以被调用、传递。
- 而#define常量则是一个Compile-Time概念,它的生命周期止于编译期:在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。
- const常量存在于程序的数据段.
- #define常量存在于程序的代码段。
优缺点:
至于两者的优缺点,要看具体的情况了。一般的常数应用,我个人认为#define是一个更好的选择:
从run-time的角度来看,#define 在空间上和时间上都有很好优势。
从compile-time的角度来看,类似m=t10的代码不会被编译器优化,t10的操作需要在run-time执行。而#define的常量会被合并。
但是 如果你需要粗鲁的修改常数的值,那就的使用const了,因为后者在程序中没有实际的存在.
另外在头文件中使用 #define 可以避免头文件重复包含的问题,这个功能,是const无法取代的。
const: 常量,在成员函数
后面增加一个const
。不单要在成员函数声明中增加const,也要在函数定义中增加const。
作用: 告诉系统,这个函数,不会修改对象里的任何成员变量的值。
成员函数后面加const的成员函数也称为“常量成员函数
”。
如果在编写const成员函数时,不小心作了修改数据成员的操作,或者调用了其它非const成员函数,编译器将指出错误,这无疑会提高程序的健壮性。
被const声明的成员函数的几点规则:
被const声明的成员函数只能访问const成员函数
,而非const函数可以访问任意的成员函数
,包括const成员函数。被const声明的成员函数不可以修改对象的数据
.它在编译时,以是否修改成员数据为依据,进行检查。mutable
修饰符的数据成员
,对于任何情况下通过任何手段都可修改,自然此时的const成员函数是可以修改它的
。普通函数(非成员函数)后面不能放const。
const意思是成员函数值不能改变,普通函数没有成员函数。const函数都能被任意类型对象调用。const 对象只能调用const函数。
class Time {
public:
int hour;
int minute;
int second;
//成员函数
public:
void addhour(int temphour) const;
};
void Time::addhour(int temphour) const {
hour += temphour;//报错,不能修改
}
class Time {
public:
int hour;
int minute;
int second;
//成员函数
public:
void addhour(int temphour) {
hour += temphour;
}
void nooe()const {};
};
const Time abc;
abc.addhour(10);//报错
abc.nooe();//正确
Time time2;
time2.addhour(10);//正确
time2.nooe();//正确
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。