define是预处理器文本替换,无类型无作用域;const/constexpr是类型安全的编译期常量;仅条件编译、头文件卫士、字符串化等少数场景必须用宏。

宏定义 #define 不是变量,也不参与类型检查
#define 是预处理器指令,在编译前就完成文本替换,不经过词法/语法分析。它没有类型、没有作用域、不占内存,只是“找字替字”。比如 #define PI 3.14159,所有出现 PI 的地方都会被原样替换成 3.14159,哪怕写成 int x = PI; 或 std::string s = PI;,预处理器也照换不误——出错要等到后续编译阶段,且错误位置指向替换后的代码,调试困难。
常见踩坑点:
-
#define MAX(a,b) (a > b ? a : b)缺少括号保护,MAX(i++, j++)会导致i或j自增两次 - 宏名不遵循命名空间,容易全局冲突(比如和头文件里同名的函数或变量撞车)
- 无法用调试器查看宏值,IDE 也无法对其跳转或补全
const 变量是真正的编译期常量,有类型、有作用域
const 声明的是具名对象,受 C++ 类型系统约束。例如 const double PI = 3.14159;,它有明确类型 double,作用域遵循块规则,可取地址(&PI),能参与模板推导,也能被优化为编译期常量(前提是初始化表达式是常量表达式)。
但要注意:
立即学习“C++免费学习笔记(深入)”;
-
const int x = rand();—— 这不是编译期常量,只是运行时只读;不能用于数组维度或模板非类型参数 -
constexpr const int y = 42;才确保是编译期常量;C++11 起推荐优先用constexpr替代简单宏 - 类内
static const成员若未在类外定义,可能链接失败(需显式定义才能取地址)
什么时候必须用 #define?其实非常少
现代 C++ 中,绝大多数原本用宏的场景都有更安全的替代方案。真正绕不开 #define 的情况只剩:
- 条件编译:
#ifdef _DEBUG、#if __cplusplus >= 201703L - 头文件卫士:
#ifndef HEADER_H、#define HEADER_H - 生成特殊标识符(如字符串化、连接符号):
#define STR(x) #x、#define CONCAT(a,b) a##b - 跨翻译单元的编译开关(如控制日志级别),且该开关需在包含头文件前就生效
其他用途——比如定义数值常量、内联小表达式、简化类型名——都应改用 constexpr、inline constexpr 或 using 别名。
宏 vs const/constexpr:性能和调试体验差异明显
从执行效率看,两者在优化后通常无差别:现代编译器对 constexpr 和简单 const 都会做常量折叠;#define 虽然早一步替换,但不会带来额外性能收益。
真正拉开差距的是工程实践:
- 宏无法被 IDE 索引,重命名、查找引用、跳转定义全部失效
- 宏展开后报错行号偏移,堆栈里看不到原始宏名
-
constexpr支持类型推导、SFINAE、if constexpr,宏完全不支持泛型逻辑 - 宏污染全局命名空间,
const和constexpr可放在namespace或class内
最易被忽略的一点:宏定义一旦出现在头文件中,就会传染给所有包含它的源文件——哪怕你只想在某个 .cpp 里临时开关一个行为,也得小心避免意外影响其他模块。










