宁可以编译器替换预处理器:
1、对于单纯的常量,最好以const对象或者 enums 替换 #defines;
2、对于形似函数的宏,最好改用 inline函数 替换 #defines。
1、const替换#define
#define ASPECT_RATIO 1.653
#define定义的常量也许从未被编译器看见,有可能没有进入记号表。而解决的方法是以一个常量来替换上面的宏:
const double AspectRatio = 1.653;
const定义的常量肯定会被编译器看到,并且会记录在记号表中;此外使用常量可能比使用宏导致更小量的码,因为预处理起会盲目的将ASPECT_RATIO替换为1.653而导致目标码中有多份1.653。
两个需要注意的情况:
1、定义常量指针:
定义常量指针的时候,有必要将指针声明为const,若要定义一个常量的字符串,那么必须写const两次:
const char* const authorName = "Scott Meyers";
而更好的表达方式是:
const std::string authorName("Scott Meyers");
2、class的专属常量
为了将常量的作用域限制在class的内部,必须让他成为class的成员,而要确保常量至多只有一份实体,必须让他成为一个static成员:
class GamePlayer{
private:
static const int NumTurns = 5; //常量的声明式
int scores[NumTurns]; //使用该常量
//...
};
C++要求要对所使用的任何东西提供一个定义式,但如果是class专属常量又是static且为整数类型,可以在声明时候直接赋值处理;但当要取某个class专属常量的地址时或者你不取地址但是编译器坚持要看到一个定义式的时候,必须另外提供定义如下:
const int GamePlayer::NumTurns = 5; //NumTurns的定义,由于声明的时候已经获得了数值初始化,这里不再赋值
也可以将初始值放在定义式,例子如下:
class CostEstimate{
private:
static const double FudgeFactor;//static class常量声明
//...
};
const double CostEstimate::FudgeFactor = 1.35;
无法利用#define创建一个class的专属常量,因为#define并不重视作用域,也不能提供任何的封装。
2、enum替换#define
基于上面的例子,当你的class在编译期间需要一个class的常量值,但你的编译器此时又不允许static整数型class常量完成class内部的初始值设定时,解决大方式:一个枚举类型的数值可以权充ints型数值使用,此时GamePlayer的定义如下:
class GamePlayer{
enum {NumTurns = 5};
int scores[NumTurns];
//...
};
这种方式既可以实现class内部常量的封装,又可以类似于宏定义一样避免常量地址的获取,用能处理编译器特殊情况不允许的问题。
3、template inline替换 #define
宏定义看起来像一个函数,但没有函数调用带来的开销,但宏有很多缺点,例如:
#define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))
注意一、要为宏中的所有实参加上小括号。
注意二、即便如此,还是会出现意想不到的问题,例如:
int a = 5, b = 0;
CALL_WITH_MAX(++a, b); //a被累加了两次
CALL_WITH_MAX(++a, b+10);//a被累加一次
解决方法是采用template inline函数,既可以获得宏带来的效率,又可以获得一般函数所有可预料的行为和类型的安全性。
template<typename T>
inline void callWithMax(const T& a, const T& b) {
f(a > b ? a : b);
}
不需要为每一个实参加上括号,也不要担心参数被非意料的计算。