const位置决定语义:左边为指针常量(p不可变),右边为常量指针(*p不可变);const成员函数承诺不修改对象状态,mutable除外;constexpr要求编译期可求值,与const本质不同。

const 放在 * 左边还是右边,直接决定是「常量指针」还是「指针常量」
很多人被「常量指针」「指针常量」绕晕,其实只看 * 的位置:如果 const 紧挨着 * 左边(比如 int* const p),那指针本身不能改;如果 const 在 * 右边(比如 const int* p 或 int const* p),那它指向的值不能改。
常见错误现象:const int* p = &x; *p = 5; 编译报错 assignment of read-only location;而 int* const p = &x; p = &y; 会报 assignment of read-only variable 'p'。
-
const T* p和T const* p等价,都是「指向常量的指针」,p可变,*p不可变 -
T* const p是「常量指针」,p初始化后不能再指向别处,但*p可改 -
const T* const p是两者叠加:指针和它指向的值都不可变 - 读法建议:从右往左读 ——
int* const p→ “p is a const pointer to int”
函数参数里加 const,不只是防改,更是接口契约
写 void foo(const std::string& s),不是怕自己手抖改了 s,而是告诉调用方:“我不会修改你传来的这个字符串”,也方便编译器做优化(比如省去隐式拷贝)。
容易踩的坑:const 引用参数不能绑定到临时对象(C++11 后放宽,但仅限于 const lvalue 引用延长生命周期,且不适用于所有场景);更隐蔽的是,如果函数内部调用了非 const 成员函数,哪怕参数是 const T&,也会编译失败 —— 因为 T 类型本身没保证 const 正确性。
立即学习“C++免费学习笔记(深入)”;
- 基本类型(
int、double)传值时加const没意义,编译器通常忽略 - 大对象(
std::vector、自定义类)优先用const T&,避免拷贝开销 - 如果函数要修改参数副本,明确写成
T值传递,而不是const T&+ 内部拷贝 - 返回
const引用需谨慎:若引用的是局部变量,就是悬垂引用
const 成员函数里,只能调用其他 const 成员函数
void bar() const 这种写法,表示该函数承诺不修改对象的任何成员变量(mutable 除外)。一旦在其中调用非 const 成员函数,或者给非 mutable 成员赋值,编译器立刻报错,比如 error: passing 'const MyClass' as 'this' argument discards qualifiers。
典型使用场景:容器类的 size()、empty()、at() 都是 const 成员函数;而 push_back()、clear() 就不是。如果你有个 const std::vector<int>& v</int>,就只能调用前者。
- const 成员函数不能修改非
mutable成员,也不能调用非 const 成员函数 -
mutable成员可在 const 函数中修改,常用于缓存、计数器等不影响逻辑状态的字段 - 重载时,const 和非 const 版本可共存:
T& operator[](size_t i);和const T& operator[](size_t i) const; - lambda 表达式默认不能修改捕获的变量,加
mutable关键字才允许(与成员函数里的mutable语义不同,但名字一样)
constexpr 和 const 不是一回事,别混用
const 只表示“运行期不可改”,constexpr 要求“编译期就能算出值”。比如 const int x = rand(); 合法(只要不取地址),但 constexpr int y = rand(); 直接编译失败。
性能影响明显:constexpr 变量可用于模板非类型参数、数组长度、static_assert 条件;而普通 const 变量做不到。但过度追求 constexpr 可能导致编译变慢或失败(尤其涉及复杂计算时)。
-
constexpr函数必须满足严格条件:函数体只能包含一条return,且所有操作必须是编译期可求值的(C++14 后放宽,允许循环、局部变量等) -
const对象不一定能用于需要常量表达式的地方,例如int arr[const_int_var];中的const_int_var必须是constexpr - 类的
constexpr构造函数要求所有成员都能用常量表达式初始化,且不能有虚函数、虚基类等 - 用
auto推导时,constexpr不会自动传播 ——constexpr int x = 42; auto y = x;中y不是constexpr
const 的边界感很关键:它不保证线程安全,不阻止 const_cast(虽然那是危险操作),也不影响对象布局。真正难的不是语法,而是判断「哪里该加、哪里不该加、加了之后别人怎么用」——这得靠对数据所有权和接口意图的持续校准。










