0 总纲
宁可以编译器替换预编译
( 源文件 -> 预处理 -> 编译 -> 汇编 -> 链接 -> 可执行文件 link )
1 单纯常量替换
- 以一个常量替换#define
Q:
#define ASPECT_RATIO 1.653
- 记号名称 ASPECT_RATIO 在预处理阶段被替换成 1.653,未被编译器看见,未计入符号表,会给调试带来麻烦
- 预处理器将 ASPECT_RATIO 盲目的替换成 1.653,可能导致目标代码出现多份1.653,代码量增多
解决之道
const double AspectRatio = 1.653;
- 作为一个常量,一定会被编译器看到
- 较小的代码量
-
两种特殊情况
- 定义常量指针
Q:
const char* const authorName = "Scott Meyers";
- 在头文件内定义一个常量的char*-based字符串,必须写const两次
解决之道
const std::string authorName("Scott Meyers");
- class专属常量
Q:
- 无法利用#define 创建一个class专属常量,因为其并不重视作用域,不能够提供任何封装性
解决之道1
class GamePlayer { private: static const int numTurns = 5; int scores[numTurns]; }
- 在一个class内定义一个表示数组的大小(数组在编译期间必须知道其大小)的常量,需要用到static 和 const 的组合
- 只要不取其地址,就可以如上述声明并使用他们,无需提供定义式;但如果需要取其地址,必须另外提供定义式(在实现文件中)如下:
const int GamePlayer::numTurns; //声明时已经赋初值
解决之道2
class GamePlayer { private: enum { numTurns = 5 } int scores[numTurns]; }
"the enum hack" 补偿法
- enum hack 的行为比较像#define, 取一个enum的地址不合法 且 不会导致非必要的内存分配
2 形似函数的宏替换
Q:
#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<typename T>
inline void callWithMax(const T& a, const T& b)
{
f(a > b ? a : b);
}
用 template inline 函数 实现, 既可以获得宏带来的效率,有可以享有一般函数所有可预料的行为和类型安全
3 预处理真香时刻
我们对预处理器的需求降低了,但是并非完全消除
#include是必须品
#ifdef/#ifndef 扮演控制编译的重要角色