题目
编写程序,阐述 const 成员函数的作用契约(承诺不修改对象内部任何逻辑状态,修饰内部隐式指针为 const ThisClass* const this)。重点展示 mutable(可变的)关键字的核心应用场景:当一个成员变量纯属内部性能优化实现细节(如访问缓存结果 cacheResult、调试统计计数器 accessCount 或线程安全互斥锁 std::mutex)时,用 mutable 修饰使其即便在 const 成员函数中依然能够合法被修改。
输入格式:无需外部输入。
输出格式:输出 const 对象在调用 const 成员函数时 mutable 缓存与访问计数器的自增变化。
数据范围与约定:物理不可变性(Bitwise constness)与逻辑不可变性(Logical constness)。
思路与算法
1. 物理不可变 vs 逻辑不可变
C++ 编译期默认强制“物理不可变性”(类的每一个 bit 都不可更改)。但实际工程中常需要“逻辑不可变性”(对用户而言对象状态没变,但内部做了一个结果缓存或记录了调用频次),这正是 mutable 的诞生初衷。
2. 互斥锁的终极修饰符
若一个只读查询函数(如 double getBalance() const)需要加互斥锁保证并发安全,因为加锁 mtx.lock() 必须改变互斥锁内部状态,互斥锁成员必须声明为 mutable std::mutex mtx_;,否则无法在 const 函数中加锁!
3. 重载区分
类可以同时提供 const 与非 const 版本的成员函数重载,编译器依据调用者对象是否具备常量性精确匹配。
完整程序
#include <iostream>
#include <string>
class HeavyCalculator {
private:
int baseValue_;
// mutable 修饰:允许在 const 成员函数中就地修改内部实现缓存
mutable int cachedResult_;
mutable bool isCacheValid_;
mutable int accessCounter_;
public:
explicit HeavyCalculator(int base)
: baseValue_(base), cachedResult_(0), isCacheValid_(false), accessCounter_(0) {}
// const 成员函数:承诺逻辑上不改变计算器基数 baseValue_
int compute() const {
++accessCounter_; // 允许:修改 mutable 调试计数器
if (!isCacheValid_) {
std::cout << " [耗时计算] 缓存未命中,执行深度数学推演...\n";
cachedResult_ = baseValue_ * baseValue_ + 42; // 修改 mutable 缓存
isCacheValid_ = true;
} else {
std::cout << " [极速读取] 直接命中 mutable 预热缓存\n";
}
return cachedResult_;
}
int getAccessCount() const {
return accessCounter_;
}
};
int main() {
// 实例化为一个纯 const 常量对象
const HeavyCalculator calc(10);
std::cout << "--- 1. 常量对象首次调用 compute() ---\n";
int res1 = calc.compute();
std::cout << "首次计算结果: " << res1 << "\n\n";
std::cout << "--- 2. 常量对象二次调用 compute() ---\n";
int res2 = calc.compute();
std::cout << "二次计算结果: " << res2 << "\n\n";
std::cout << "该 const 实例已被调用访问次数: " << calc.getAccessCount() << " 次\n";
return 0;
}运行示例
输出:
--- 1. 常量对象首次调用 compute() ---
[耗时计算] 缓存未命中,执行深度数学推演...
首次计算结果: 142
--- 2. 常量对象二次调用 compute() ---
[极速读取] 直接命中 mutable 预热缓存
二次计算结果: 142
该 const 实例已被调用访问次数: 2 次