const成员函数锁住的是this指针所指对象的可修改性,禁止修改成员变量、调用非const成员函数或返回非常量引用,但允许mutable变量修改。

const 成员函数到底锁住了什么
它锁住的是 this 指针所指对象的**可修改性**,不是函数内部所有变量。也就是说,编译器会禁止你在 const 成员函数里调用非 const 成员函数、修改成员变量(除非加 mutable),甚至不能返回成员变量的非常量引用。
常见错误现象:error: assignment of member 'X::val' in read-only object —— 你在 const 函数里写了 val = 42;;或者 error: passing 'const X' as 'this' argument discards qualifiers —— 你试图在 const 函数里调用一个没加 const 的成员函数 helper()。
- 只读场景才该加 const:比如
size()、empty()、at()(标准库中这些全是 const) - 如果函数逻辑上不改变对象状态,但内部用了缓存或日志计数器,把对应变量声明为
mutable int cache_hits_;即可绕过限制 - const 成员函数可以被 const 对象和非常量对象同时调用;而非 const 版本只能被非常量对象调用
const 放哪儿?语法位置别搞错
const 是成员函数声明的**一部分**,必须紧挨着函数参数列表之后、函数体之前,且属于函数类型签名。写错位置(比如放在返回类型前、或者函数名后空格太多)会导致编译失败或语义完全不同。
正确写法:int get_value() const;;错误写法:const int get_value();(这只是返回 const int)、int get_value() const & {}(多了一个 &,变成右值引用限定符)。
立即学习“C++免费学习笔记(深入)”;
- 声明和定义必须一致:头文件里写了
void log() const;,实现文件里就得写void X::log() const { ... } - const 修饰的是隐式
this的类型:在 const 成员函数内,this的类型是const X*,不是X* - 如果函数还带引用限定符(C++11 起),顺序是:参数列表 →
&或&&→const(如void f() & const;合法但极少用,通常只选其一)
const 成员函数和重载的关系
const 和非 const 版本可以构成重载——这是 C++ 允许的,而且非常实用。编译器根据调用对象是否为 const 来决定调用哪个版本,而不是看函数内部干了啥。
典型使用场景:实现 operator[] 的两种行为。非常量对象返回可写引用,const 对象返回只读引用,避免意外修改。
class Vec {
std::vector<int> data_;
public:
int& operator[](size_t i) { return data_[i]; }
const int& operator[](size_t i) const { return data_[i]; }
};
- 如果只提供非 const 版本,const 对象就无法调用
operator[],连读都不行 - 如果只提供 const 版本,非常量对象也能调,但返回的是 const 引用,无法赋值,失去意义
- 两个版本返回类型必须不同(通常是引用 vs const 引用),否则重载解析失败
容易被忽略的隐式转换陷阱
const 成员函数不会阻止你调用某些看似“安全”的操作,但可能触发静默的临时对象构造,带来性能或逻辑问题。
例如:在 const 成员函数里返回 std::string 成员的 c_str(),看起来没问题,但如果成员是 std::string name_,而你写 return name_.c_str();,返回的是指向内部缓冲区的指针——这本身合法;但如果你不小心写了 return (name_ + "_tmp").c_str();,就会返回临时 std::string 的内部指针,一出函数就悬垂。
- const 成员函数里调用非 const 成员函数不会“自动转 const”,只会报错——别指望编译器帮你妥协
- 返回值类型如果是类类型,确保拷贝/移动语义明确;返回
const T&要确认引用目标生命周期足够长 - lambda 捕获
this时,若在 const 成员函数里,捕获的是const X*,lambda 内部也不能改成员(除非用const_cast,不推荐)
最麻烦的不是语法报错,而是那些编译通过但行为异常的情况:比如缓存没更新、日志漏打、或返回了悬垂指针。const 的边界很清晰,但它的“保护范围”只到成员访问这一层,不延伸到你调用的每个第三方函数里。










