mutable关键字
mutable允许在const对象中修改成员,支持逻辑上的“可变”状态更新。
mutable关键字
mutable 关键字
在 C++ 中,mutable
是一个类型修饰符,允许类的成员变量在 const
对象 或 const
成员函数 中被修改。
- 用途:修改内部缓存、统计访问次数等,但不改变类的逻辑状态(logical constness)。
- 原因:
const
成员函数保证对象逻辑上不变,但mutable
允许在物理上修改部分内部数据。
基本语法
1
2
3
4
5
6
7
8
class MyClass {
public:
mutable int counter;
void increase() const {
++counter; // 合法,尽管是 const 函数
}
};
counter
被声明为mutable
,即使是在const
成员函数中,也允许对它进行修改。increase()
是const
函数,它不能修改除mutable
成员以外的任何数据成员。
使用场景
在 const 成员函数中做缓存或懒加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 一个带缓存功能的类,使用 mutable 实现懒计算(lazy evaluation)
class CachedValue {
private:
// 标志变量:表示是否已经计算过结果
// 因为会在 const 函数中修改,必须加 mutable 修饰
mutable bool computed = false;
// 缓存的计算结果,初始值为 0
// 也是需要在 const 函数中被修改,所以加 mutable
mutable int cache = 0;
public:
// 返回计算值的函数,逻辑上是 const 的:对外接口不会改变对象的状态
int getValue() const {
// 如果还没计算过,就计算一次并缓存结果
if (!computed) {
cache = expensiveCalculation(); // 修改了 mutable 成员
computed = true; // 标记为已计算
}
// 返回缓存的结果
return cache;
}
private:
// 实际的计算逻辑(模拟耗时操作)
int expensiveCalculation() const {
return 42;
}
};
虽然
getValue()
是const
,但它需要修改computed
和cache
,这些变量被声明为mutable
以绕过编译器的限制。这是典型的懒计算缓存模式。
日志记录或统计访问次数
1
2
3
4
5
6
7
8
9
10
class Logger {
private:
mutable int accessCount = 0;
public:
void print() const {
++accessCount;
std::cout << "Access #" << accessCount << std::endl;
}
};
和 const_cast 区别
特性 | mutable | const_cast |
---|---|---|
用途 | 成员变量在 const 对象中可修改 | 去除对象的 const 修饰符 |
编译期安全 | 是 | 否,若修改真正 const 对象会导致未定义行为 |
推荐使用程度 | 安全合理使用没问题 | 小心使用,容易出错 |
常见陷阱
不要滥用
mutable
。它破坏了const
的“承诺”,应仅在有充分理由的情况下使用。线程安全问题。如果
mutable
成员在多线程下被修改,可能导致竞态条件。mutable
只能用于类的非静态成员变量,不能修饰局部变量、静态变量、函数或参数等。为什么不能用于局部变量?
局部变量本身没有
const
对象约束,根本就不需要mutable
:1 2 3
void func() { mutable int x = 0; // 编译不通过 }
局部变量随时可以修改,它从来不会受到
const
的约束,所以mutable
没有任何意义,自然也被语言禁止使用。为什么不能用于函数?
函数不是变量,
mutable
修饰的是变量的可变性,不能用于函数。1
mutable int getValue(); // 语法错误
如果想表达“这个函数在
const
对象上也能调用”,应该用const
修饰函数,而不是mutable
:1
int getValue() const; // 表示此函数不会修改对象状态
为什么不能用于函数参数?
因为函数参数也是局部变量,它们没有
const
对象语义,也不需要mutable
。1
void func(mutable int x); // 错误
而如果希望参数不可修改,可以用
const
修饰:1
void func(const int x); // 表示 x 不可修改(传值没意义,传引用才重要)
为什么不能用于静态成员变量?
静态成员变量是类级别的变量,不依赖于具体对象,不受
const
对象的影响。1 2 3
class A { mutable static int count; // 错误,mutable 不能用于 static 成员 };
const
对象是指某个具体对象不可修改,而static
变量与具体对象无关。- 所以,即使某个对象是
const
,依然可以修改类的static
变量(前提是没有被const
修饰)。
1 2 3 4 5 6
class A { static int count; void modify() const { ++count; // 合法,即使是在 const 函数中 } };
本文由作者按照 CC BY 4.0 进行授权