const 锁住的是变量名与对象的绑定关系而非值本身;指针的 const 位置决定修饰目标;成员函数 const 表示不修改非 mutable 成员;const_cast 仅用于对接 C API 且不可写入真正 const 内存;constexpr 要求编译期求值,const 不要求。

const 修饰变量时,到底锁住了什么
它锁住的是「绑定关系」,不是值本身——一旦初始化完成,const 变量名就不能再指向别的对象或内存位置。但若类型本身可变(比如 std::vector<int>),其内部元素仍可修改,除非你用 const std::vector<int> 或更严格的 const std::vector<int>&。
常见错误现象:const int* p = &x; 被误以为是“p 是 const”,其实它是“p 指向的 int 是 const”;真正让指针本身不可变的是 int* const p = &x;;两者都不可变才是 const int* const p = &x;。
- 想保护数据不被意外改写 → 用
const T(如const std::string) - 想防止指针重定向 → 用
T* const - 函数参数传只读大对象 → 优先用
const T&,避免拷贝又禁止修改 - 返回局部对象时别返回
const T&→ 会悬空引用
成员函数加 const 的真实含义
加在函数声明末尾的 const,表示该函数承诺不修改 *this 对象的任何非 mutable 成员。编译器会据此拒绝调用非常量成员函数、或对非 mutable 成员赋值。
使用场景:当你需要在 const 对象上调用某个方法(比如 obj.size()),或者希望接口明确表达“这个操作是只读的”,就必须加 const。否则哪怕逻辑上没改状态,也会编译失败。
立即学习“C++免费学习笔记(深入)”;
- const 成员函数里不能调用非常量成员函数
- const 成员函数里不能修改非
mutable成员变量 -
mutable成员可用于缓存、计数等“逻辑上不改变对象语义”的场景 - 重载时,
void foo() const和void foo()是两个不同函数,可分别实现
const_cast 不是“去掉 const”,而是“绕过 const 正确性检查”
const_cast 唯一合法用途是对接 C 风格 API(比如把 const char* 强转成 char* 传给旧库),且前提是那个底层内存确实 *本来就不带 const 语义*。对真正由 const 变量定义的内存做写入,属于未定义行为。
容易踩的坑:const_cast 后写入导致崩溃或静默错误,尤其在优化开启(-O2)时,编译器可能把 const 变量当常量折叠,根本不出现在内存里。
- 不要用
const_cast修改字面量或const全局变量 - 不要用它来“临时绕过 const 约束”做业务逻辑
- 如果发现自己频繁需要
const_cast,大概率是接口设计有问题 - 替代思路:用
mutable、重构为非 const 接口、或拆分 const/non-const 版本
constexpr 和 const 在编译期行为上的关键区别
const 只保证运行时不可修改,而 constexpr 要求必须能在编译期求值。一个 const int x = rand(); 合法,但 constexpr int y = rand(); 直接报错。
性能影响明显:只有 constexpr 变量才能用作模板非类型参数、数组大小、case 标签等需要编译期常量的地方。
-
const变量可能是运行时初始化(如构造函数中赋值) -
constexpr函数必须有constexpr参数,且所有分支都得能编译期执行 - C++20 起,
constexpr支持更多运行时操作(如动态内存分配),但实际能否编译期求值仍取决于调用上下文 - 别为了“看起来更现代”盲目把所有
const换成constexpr,会限制使用场景
const 的复杂点不在语法,而在它和对象生命周期、内存模型、编译器优化的耦合。最容易被忽略的是:同一个 const 修饰,在变量、指针、成员函数、模板参数中扮演的角色完全不同,不能靠直觉类推。










