《Effective C++》rule 02: Prefer consts, enums, and inlines to #defines
宏定义的问题
C++ 会在预处理阶段对宏定义进行字符串替换.
因此,如果在一个头文件进行了类似如 #define ASPECT_RATIO 1.653
的宏定义,那么此常量相关的编译错误信息显示的会是 1.653, 而这个宏定义如果不在你写的程序内 (而是它包含的一个头文件内),那么定位问题就会很麻烦.
原因很简单: 宏定义在预处理阶段就被处理,因此使用的名称并没有进入符号表 (symbol table) 中.
如何解决
对于常量,可以使用 const 来代替 #define:
const double AspectRatio = 1.653;
这里,常量 AspectRatio
会进入符号表中,所以会被编译器"看见".
使用常量还有一个好处: 相比 #define 的目标码 (object code) 更小.
因为宏定义是进行字符串替换,在目标码中会进行多次替换;而使用 const 则没有这个问题.
#define 的另一个误用情况
另一个常见的 #define 的误用情况是用它实现 宏 (macros).
宏看起来像函数,但没有函数调用 (function call) 带来的额外开销.
例如:
// 在 a 和 b 的较大值中调用 f
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) :(b))
由于宏是进行字符串替换,所以写这种宏的时候要记得给所有实参加上小括号,否则可能在调用宏的时候出现错误 (不可预料的行为和类型不安全).
为了使用宏带来的效率同时保证函数的所有可预料行为和类型安全性 (type safety),可以使用 template inline 函数.
例如上面的例子可以改为:
/*
因为不确定 T 是什么类型,所以这里使用 pass by reference to const 的形式
*/
template<typename T>
inline void callWithMax(const T& a, const T& b) {
f(a > b ? a : b);
}
可以发现,这里不需要给参数都加上括号.
这里的 callWithMax
是个真正的函数,它有作用域 (scope) 和函数的访问规则,而宏就没有这些东西.
总结
- 对于单纯的常量,最好用
const
对象或enum
替换#define
- 对于形似函数的宏 (macros),最好用
inline
函数替换#define
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现