Effective C++ 条款02:尽量以 const, enum, inline 替换 #define
Effective C++ 条款02:尽量以 const
, enum
, inline
替换 #define
核心思想:使用编译器可处理的实体代替预处理器宏,避免宏的副作用,增强类型安全性和调试能力。
主要问题及解决方案:
问题类型 | #define 的缺陷 | 替代方案 | 优势 |
---|---|---|---|
常量定义 | 无类型检查、不进入符号表、作用域不受控 | const 常量 | 类型安全、可调试、作用域控制 |
类内常量 | 无法在类内初始化静态整型常量 | enum 枚举常量 | 不占用内存、编译器可见、类作用域 |
函数宏 | 易引发多次求值、参数副作用、无类型检查 | inline 函数 | 类型安全、避免多次求值、可调试 |
代码示例
1. 用 const
替换常量宏
// 不良实践:宏常量
#define PI 3.14159
#define MAX_BUFFER 1024// 改进方案:const常量
const double kPi = 3.14159; // 类型明确
const int kMaxBuffer = 1024; // 进入符号表
const char* const kAuthor = "Scott Meyers"; // 常量指针// 类作用域常量
class Circle {
private:static const int kDefaultRadius = 10; // 类内初始化(整型)const double kScaleFactor; // 成员常量
public:Circle() : kScaleFactor(1.5) {} // 初始化列表初始化
};
2. 用 enum
实现类内常量
class GamePlayer {
private:// 当编译器不允许静态整型常量类内初始化时enum { kMaxPlayers = 6 }; // enum hackint scores[kMaxPlayers]; // 编译期确定数组大小public:void printMax() {// kMaxPlayers 不会分配内存,无法取地址std::cout << "Max players: " << kMaxPlayers; }
};// 模板元编程中的enum hack
template<int N>
struct Factorial {enum { value = N * Factorial<N-1>::value };
};
template<>
struct Factorial<0> {enum { value = 1 };
};
3. 用 inline
替换函数宏
// 危险宏:参数可能被多次求值
#define SQUARE(x) ((x) * (x)) // 改进方案1:模板内联函数
template<typename T>
inline T square(const T& x) { return x * x;
}// 改进方案2:带作用域的宏替代
inline int safeSquare(int x) { return x * x;
}// 测试案例
int main() {int a = 5;// 宏的陷阱:SQUARE(++a) -> ((++a)*(++a)) = 7*7=49 (a=7)std::cout << "Macro: " << SQUARE(++a) << ", a=" << a << "\n"; a = 5;// 安全调用:square(++a) -> 6*6=36 (a=6)std::cout << "Inline: " << square(++a) << ", a=" << a; return 0;
}
输出:
Macro: 49, a=7 // 不符合预期的结果
Inline: 36, a=6 // 符合预期
关键总结
场景 | 替代方案 | 核心优势 |
---|---|---|
全局/命名空间常量 | const 常量 | 类型安全、调试可见、作用域控制 |
类内整型常量 | enum 枚举常量 | 不分配内存、避免静态初始化顺序问题 |
类内非整型常量 | static const + 类外定义 | 类型安全、符合标准 |
函数式宏 | inline 函数(或模板函数) | 避免多次求值、类型检查、支持作用域规则 |
编程启示:
- 宏在预处理阶段被替换,编译器看不到宏符号,增加调试难度
- 优先使用编译器可见实体(
const
,enum
,inline
) enum hack
在模板元编程和资源受限环境中仍有价值- 现代 C++ 中
constexpr
可进一步替代部分场景(C++11 起)