Files
CPlusPlusThings/site_extras/cpp-things-cpp-mutable-keyword.md
light-city dfe1c933b3 docs: address community issue feedback (#5 #338 #142 #246 #136 #303)
- const README: rewrite the space-saving claim per issue #5 follow-ups
  (constant-expression const vs #define immediates; which const qualify
  as constant expressions)
- const README: fix the class-section diagnosis per issue #338 (overload
  resolution picks the const overload; the restriction only surfaces
  once that overload is removed)
- sizeof README: virtual-inheritance result is compiler-dependent
  (GCC merges vbptr+vfptr -> 16, MSVC keeps them separate -> 32) #246
- static README: new section on internal linkage / access restriction
  with anonymous-namespace comparison #142
- 10-day practice: full problem list table + fixed day links #136
- new design_pattern/singleton/singleton_default.hpp: reusable
  thread-safe CRTP singleton template, compile+concurrency tested #303
2026-10-13 19:23:54 +08:00

75 lines
4.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
> 来自 [C++那些事网站](https://light-city.github.io/stories_things/cpp-things/cpp-mutable-keyword/) 的文章,由站点同步脚本导出。
# C++ mutable 关键字
### 深入探讨 C++ 中的 `mutable` 关键字
在 C++ 中,`mutable` 关键字经常被认为是“穿透”常量(`const`)限制的工具。在某些情况下,虽然我们希望对一个对象施加常量保护,但还是需要对该对象的某些数据成员进行修改。本文将详细探讨 `mutable` 的使用场景和背后的设计哲学,并讨论其如何在 C++ 中解决特定的编程需求。
#### 1. `mutable` 关键字的基本用途
我们知道,当一个成员函数被 `const` 修饰时,该成员函数保证不会修改对象的状态。然而,在实际编程中,有时我们希望修改一个数据成员,但仍然希望将成员函数标记为 `const`。这就是 `mutable` 关键字的作用:
```cpp
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const {
// 在 const 成员函数中修改 mutable 成员
done_ = true;
}
};
```
在上面的例子中,`doSomething()` 被声明为 `const`,表明它不会改变对象的状态。然而,`done_` 被标记为 `mutable`,这意味着即使在 `const` 方法中,我们仍然可以修改 `done_`。这种设计使得我们可以在保持函数接口语义不变的情况下,管理某些需要修改的数据。
#### 2. 逻辑常量性 vs. 位常量性
C++ 中的常量性可以分为两类:**逻辑常量性** 和 **位常量性**。逻辑常量性指的是对象的状态在外部看来没有发生变化,而位常量性指的是对象在内存中的位数据保持不变。`mutable` 关键字允许我们定义逻辑常量性,即即使对象的某些内部数据变化了,但在逻辑上对象的状态并未发生可见变化。
比如在一个线程安全的类中,我们可能会使用 `boost::mutex` 来保护数据访问。为了在 `const` 方法中获取锁,我们可以将 `mutex` 声明为 `mutable`:
```cpp
class ThreadSafeClass {
private:
mutable std::mutex mtx_;
int data_;
public:
int getData() const {
std::lock_guard<std::mutex> lock(mtx_);
return data_;
}
};
```
在上述例子中,虽然 `getData` 方法被声明为 `const`,但我们需要锁来保护数据访问的线程安全性。因此,我们将 `mtx_` 声明为 `mutable`,以允许在 `const` 方法中对其进行修改。这种情况下,我们说类的逻辑状态没有改变,只是为确保线程安全而进行的修改。
#### 3. 在 Lambda 表达式中的 `mutable`
从 C++11 开始,`mutable` 也可以用于 lambda 表达式,以允许修改按值捕获的变量。默认情况下,lambda 中按值捕获的变量是不可变的。如果需要在 lambda 中修改捕获的变量,可以使用 `mutable`:
```cpp
int x = 0;
auto f1 = [=]() mutable { x = 42; }; // OK: 可以修改捕获的 x
auto f2 = [=]() { x = 42; }; // Error: 按值捕获的变量不能被修改
```
这种情况下,`mutable` 允许我们在函数体内修改捕获的变量,而不会影响到外部变量的值。
#### 4. 避免使用 `const_cast`
没有 `mutable` 关键字的情况下,我们可能需要使用 `const_cast` 来处理这些特殊情况。`const_cast` 可以去掉对象的常量性,但它也有缺点:`const_cast` 可以完全移除常量性保护,让我们对对象进行任意的修改和方法调用,这会导致代码的可读性和安全性下降。
相比之下,`mutable` 关键字提供了一种更细粒度的控制方式。通过 `mutable`,我们可以在特定的数据成员上设置“例外”,允许在 `const` 方法中修改,而不必将整个对象的常量性去掉。因此,`mutable` 能够更安全地处理这些特例。
#### 5. 使用 `mutable` 的设计哲学
`mutable` 关键字的设计哲学在于保持接口的**逻辑常量性**。在大多数情况下,我们希望编写 const-correct 的代码,这意味着对象的状态在没有显式修改的情况下不会变化。`mutable` 允许我们在某些内部实现上做出让步,比如缓存、引用计数、调试信息等,而不会打破这种接口设计的一致性。
设计一个接口时,我们应该明确何时以及为什么需要修改一个对象的状态。`mutable` 关键字能让代码在逻辑层面保持不变,同时又提供了修改特定数据成员的能力。这需要设计者深思熟虑,确保 `mutable` 的使用不会破坏对象的常量性概念,只有在确实需要的情况下使用。
### 6.参考资料
> https://stackoverflow.com/questions/105014/does-the-mutable-keyword-have-any-purpose-other-than-allowing-a-data-member-to