在C++的面向对象编程中,我们经常会用const修饰成员函数,来保证该函数不会修改对象的成员变量状态,提升代码的安全性和可读性。但有些场景下,我们希望在const成员函数中修改某些特定的成员变量,比如用于缓存计算结果、记录对象被访问的次数等,这时候就需要用到mutable关键字。

mutable关键字的基本作用
mutable是C++中的一个存储类说明符,只能用于类的非静态成员变量声明前。被mutable修饰的成员变量,即使在const成员函数中,也可以被修改,不受const成员函数的限制。需要注意的是,mutable不能修饰静态成员变量,也不能修饰普通变量或者函数参数。
基础使用示例
我们先来看一个最简单的使用场景,在const成员函数中修改mutable成员变量:
#include <iostream>
#include <string>
class User {
private:
std::string name;
mutable int access_count; // 被mutable修饰的成员变量,记录访问次数
public:
User(const std::string& n) : name(n), access_count(0) {}
// const成员函数,默认不能修改普通成员变量
std::string get_name() const {
access_count++; // 修改mutable成员变量,合法
return name;
}
int get_access_count() const {
return access_count;
}
};
int main() {
const User user("张三"); // 定义const对象
std::cout << "用户名:" << user.get_name() << std::endl;
std::cout << "访问次数:" << user.get_access_count() << std::endl;
std::cout << "再次访问用户名:" << user.get_name() << std::endl;
std::cout << "访问次数:" << user.get_access_count() << std::endl;
return 0;
}
上面的代码中,access_count被声明为mutable,所以即使在get_name这个const成员函数中,也可以对其进行自增操作。同时user是const对象,只能调用const成员函数,这时候mutable的作用就体现出来了。
mutable的典型使用场景
场景一:缓存计算结果
当某个成员函数的计算结果比较耗时,且多次调用时结果不会变化时,可以用mutable变量缓存结果,避免重复计算:
#include <iostream>
#include <vector>
#include <algorithm>
class DataProcessor {
private:
std::vector<int> data;
mutable bool is_sorted; // 标记数据是否已经排序
mutable std::vector<int> sorted_cache; // 缓存排序后的结果
public:
DataProcessor(const std::vector<int>& d) : data(d), is_sorted(false) {}
// const成员函数,获取排序后的数据
const std::vector<int>& get_sorted_data() const {
if (!is_sorted) {
sorted_cache = data;
std::sort(sorted_cache.begin(), sorted_cache.end());
is_sorted = true; // 修改mutable标记的状态变量
}
return sorted_cache;
}
};
int main() {
const DataProcessor processor({3, 1, 2});
const std::vector<int>& sorted = processor.get_sorted_data();
for (int num : sorted) {
std::cout << num << " ";
}
// 再次调用时直接返回缓存结果,不需要重新排序
const std::vector<int>& sorted2 = processor.get_sorted_data();
return 0;
}
场景二:记录对象状态统计信息
除了缓存,mutable还常用于记录对象的访问次数、修改次数等统计信息,这些信息不影响对象的核心逻辑状态,属于辅助性的状态:
#include <iostream>
class Counter {
private:
mutable int read_count;
mutable int write_count;
int value;
public:
Counter(int v) : read_count(0), write_count(0), value(v) {}
int get_value() const {
read_count++;
return value;
}
void set_value(int v) {
write_count++;
value = v;
}
int get_read_count() const { return read_count; }
int get_write_count() const { return write_count; }
};
使用mutable的注意事项
- mutable只能修饰类的非静态成员变量,不能修饰静态成员变量,也不能修饰普通变量、函数参数或者返回值。
- 被mutable修饰的成员变量,即使在const对象中也可以被修改,所以不要滥用mutable,否则会破坏const的语义,让const对象的状态变得不可控。
- 如果类的成员变量涉及到线程安全,使用mutable修改时需要额外加锁,避免多线程下的数据竞争问题。
- mutable不能和const同时修饰同一个成员变量,因为mutable本身就是打破const限制的,两者同时修饰会产生语义冲突。
常见误区说明
有些开发者会误以为mutable可以修改所有成员变量,实际上只有被显式声明为mutable的成员变量才能在const成员函数中修改。普通成员变量即使是在const成员函数中尝试修改,编译器也会直接报错。
另外要注意,const成员函数修饰的是this指针指向的对象为const,所以mutable修改的是当前对象的成员,不会影响其他对象的状态,这一点和静态成员变量是不同的,静态成员变量属于类所有,不属于某个具体对象,所以不能用mutable修饰。