相信大家在使用c++编程中都会有这样的疑惑,为什么c++建议使用const/enum/inline替换#define呢?
可以这样来理解:使用编译器来替换预处理器。问题就在于因为#define不被视为语言的一部分。
1.那么什么时候使用const替换宏呢?
比如:#define PI 3.1415
宏定义名称PI有两种可能性:也许从未被编译器看见,也许在编译器开始处理源码之前它就被预处理器移走了。就会导致PI有可能没进入记号表(symbol table),于是当运用此变量时就会获得一个编译错误信息时,就会带来困惑。错误信息提示的是3.1415而不是PI,就会迷惑程序员错误的位置和追踪错误信息而导致浪费时间。问题也有可能出在记号式调试器中,原因就是未进入记号表(symbol table)。
最好的解决方法就是以常量替代宏(#define):
const double pi = 3.1415; //定义为一个const的变量 不可改变
而此时PI变量就作为一个语言常量,就会被编译器所看到,当然就会进入记号表内,就不出现上述所出现的问题。
当我们使用常量替换#define时,有两个特殊的情况需注意:
①定义常量指针时,假如要在头文件内定义一个常量的(不变的)char *str字符串,必须写const两次。
const char *const strname = "helloworld";
//const和指针的应用就不解释了(主要区分常量指针和指向常量的指针)
值得注意的是c++中string对象通常比char *str更合宜,更适用:
const std::string strname = "helloworld";
②class专属常量,为了将常量的作用域(scope)限制于class内,就必须改为class的一个成员(member),而为此保证此常量至多只有一份实体,就必须定义为static成员:
class GamePlayer
{
private:
static const int NumTurns = 5;//注意这儿是声明式 不是定义式
//注意:旧式编译器是不支持上述定义的,因为不允许static成员在其声明式上获取初值的。
int scores[NumTurns]; //使用常量
...
};
通常C++要求你对所使用的任何东西提供一个定义式,但如果是class专属常量又是static且为整数类型,那么需要特殊处理,只不取它们的地址,就可以不用提供定义式。如果需要取常量的地址,那么就另外提供定义式如下:
const int GamePlayer::NumTurns;//常量的定义
//为什么没有给予赋值
//该式子放进实现文件而非头文件,由于class常量已在声明时获得初值,因此定义时不需要再设置初值。
为什么我们不使用#define定义class专属常量?
因为#define并不重视作用域(scope),一旦宏被定义,后面的编译过程都有效(除非被#undef),因此宏定义是不能够提供任何封装性的。但const是可以被封装的。
2.那什么情况下又需要enum枚举类型来替换#define呢?
如上述的数组GamePlayer::scores的数组声明中,编译器坚持必须在编译器期间知道数组的大小。这个时候万一你的编译器(错误地)不允许“static整数型class常量”完成“in class 初值设定”,就可以用“the enum hack”补偿做法。
理论基础:“一个属于枚举类型数组可权充ints被使用”。定义如下:
class GamePlayer
{
private:
enum {NumTurns = 5};
int scores[NumTurns];
...
};
理由:①enum hack的行为某方面说比较像#define而不像const。(如取一个enum的地址是不合法的)
②纯粹是为了实用主义。许多代码用了它,所以看到它时你必须认识它。
3.那么什么情况下需要使用inline替换宏呢?
实现宏#define时,程序员在调用以及实现时会经常误用。
如:
#define CALL_MAX(a,b) f((a) > (b) ? (a) : (b))
//这种情况下的宏有太多缺点,如果使用递增++ 递减--就会让人很头疼。
CALL_MAX(++a,b); //如果a > b,a被累加两次
CALL_MAX(++a,b); //如果a < b, a被累加一次
这个时候就可以写出template inline函数来使用,不仅可以获得宏带来的效率以及一般函数的所有可预料行为和类型安全性。
template<typename T> inline void callmax(const T&a,const T&b)
{
f(a > b ? a : b);
}
//这里不需要在函数本体中为参数加上括号,也不需要操心参数被核算多次等等,也遵守作用域和访问规则。
使用技巧:
对于单纯常量,最好以const对象或enums替换#define。
对于形似函数的宏,最好改用inline函数替换#define。
希望能够帮助到大家。
记住一定要多看技术类型的书籍,对编程是非常有用的。