mutable修饰的变量能在const成员函数中被修改,用于缓存、日志等不影响逻辑状态的内部数据;不能用于static、const、引用成员;不保证线程安全,需配合原子类型或锁使用。

mutable 修饰的变量能在 const 成员函数里被修改
const 成员函数承诺不修改对象的逻辑状态,但编译器只检查「非 mutable 成员是否被赋值」。只要变量声明为 mutable,哪怕在 const 函数里也能读写——它绕过了 const 正确性的静态检查。
典型用途是缓存、引用计数、日志标记这类不影响对外可见状态的内部辅助数据:
class Cache {
mutable std::map cache_;
mutable bool logged_ = false;
public:
std::string get(int key) const {
auto it = cache_.find(key);
if (it != cache_.end()) return it->second;
// 插入操作合法:cache_ 是 mutable
return cache_[key] = expensive_compute(key);
}
void log_access() const {
if (!logged_) {
std::cout << "First access\n";
logged_ = true; // ✅ 允许修改
}
}
};
mutable 不能用在 static 或 const 成员上
mutable 只作用于**非静态、非常量、非引用、非 const 限定的普通数据成员**。下面这些写法都是非法的:
-
mutable static int x;→ 编译错误:static成员不属于单个对象,mutable无意义 -
mutable const int y = 42;→ 错误:const 变量本身不可修改,mutable无法覆盖该约束 -
mutable int& ref;→ 错误:引用必须初始化,且绑定后不可重绑,mutable不改变这一语义 -
mutable std::unique_ptr→ 合法,但注意:p; reset()或swap()是允许的,因为它们修改的是指针值,不是所指对象
mutable 和 thread_local / static local 混用要小心
mutable 解决的是 const 成员函数内的可变性问题,但它不提供线程安全。如果你在 const 函数里修改 mutable 成员(比如更新一个计数器),多个线程并发调用时会引发数据竞争:
立即学习“C++免费学习笔记(深入)”;
class Counter {
mutable int count_ = 0;
public:
void tick() const { ++count_; } // ❌ 非原子操作,多线程下未定义行为
};
需要同步时,得自己加锁或改用原子类型:
- 用
mutable std::atomic_int count_;,然后count_.fetch_add(1); - 或
mutable std::mutex mtx_;+std::lock_guard,但注意:锁对象本身也得是mutable才能在 const 函数里上锁
const_cast 强转 this 指针不是替代方案
有人想绕过 const 限制,用 const_cast。这属于未定义行为(UB)——除非原对象本身是非 const 的。而 mutable 是唯一被标准明确允许的、在 const 成员函数中修改数据成员的机制。
容易忽略的一点:mutable 成员仍参与类的内存布局和 sizeof 计算,它不会被编译器“优化掉”;但它在 const 对象中依然可写,这点和普通 const 成员有本质区别。










