易变mutable

作为程序员,你写的程序(就拿个类来说吧)只有三种可能:
1.为程序员用户写的,供其它程序员用,自己充当功能的封装角色,就像程序库的设计者一样
2.仅仅自己用,就像把局部重复次数比较多的代码段写成函数一样
3.自己用,其它程序员也想用,毕竟重用是编程的最高境界
把一个类成员函数适时的加上const关键字对于以上三种情况都是有着积极的意义,以下是三种
情况的理由:
1.加上const关键字,程序员通过接口声明就会立即明白该函数是不会更改对象的状态,通常程
  序员可不会去看实现
2.对于一个类成员函数行为往往我们从语义上去判断它会不会改变对象的状态,如果是不应该的
  话,我们在写它的实现之前就应该设成const。类是对象的抽象,设计类的函数(行为)都是极尽
  的去模拟对象。设成const可以避免自己在编程中无意修改了对象的状态,过了一段时间它的作用
  更可以和注释一样
3.理由是1+2
说了这么多其实是为了说明mutable的作用做铺垫,明白const在修饰成员函数的作用,你才能深刻体
会到mutable是多么有必要。
mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。
在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,
即使在一个const函数中。我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会
声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么
这个数据成员就应该被mutalbe来修饰。
举个例子:
class CDog
{
   void bark(void) const{cout << "汪汪..." << endl;} //为了节省篇幅,写成内联形式的
   private:
 int m_iCount;
 /*假设狗叫一次就代表有一个陌生人来了,iCount用来记录陌生人个数,它的改变不会
   改变狗实例本身的状态。注意只是假设不能较劲,实际情况不是这样的(对一个陌生人狗
   不只叫一声吗等等) 
 */    
};
现在要计数陌生人的个数怎么办?这样:
void bark(void) const{cout << "汪汪..." << endl; ++m_iCount;}
/*注意:编译错误,还有一点就是const限制不了修改类成员变量
(静态的成员变量叫类变量,非静态的叫实例变量)*/
这样不行,那怎么办?有两种解决方案:
1.去掉bark的const修饰符
2.给计数实例变量iCount加上mutable修饰符,如下:
 mutable int m_iCount;
  计数器m_iCount被mutable修饰,那么它就可以突破const的限制,在被const修饰的函数里面也能被修改。
如果选择1,那么前面所列的种种const修饰符的好处就荡然无存,而如果选择2两者兼得,孰优孰劣?


 


 

<think>我们正在讨论C++中的`volatile`和`mutable`关键字。根据用户提供的引用,我们可以总结如下: ### `volatile`关键字 **作用**: - 防止编译器对变量进行优化(如缓存到寄存器),确保每次访问都直接从内存中读取或写入[^1][^2][^4]。 - 用于多线程、硬件寄存器等场景,确保变量的可见性和操作的原子性(但注意,`volatile`不保证原子性,只是保证每次访问都从内存读取)[^1][^4]。 **使用场景**: 1. 硬件寄存器映射(如嵌入式开发中,寄存器的值可能被硬件改变)[^1]。 2. 多线程共享变量(但需注意,`volatile`不能替代原子操作或互斥锁)[^4]。 3. 信号处理程序中修改的全局变量[^1]。 **示例**: ```cpp volatile int sensorValue; // 传感器值可能随时被硬件改变 void readSensor() { while (sensorValue == 0) { // 循环直到sensorValue改变(编译器不会优化掉此循环) } } ``` **注意事项**: - `volatile`变量间的操作顺序不会被编译器交换,但`volatile`和非`volatile`变量间的操作顺序可能被编译器优化乱序[^4]。 - C++11后,多线程场景更推荐使用`std::atomic`(提供真正的原子操作)[^4]。 --- ### `mutable`关键字 **作用**: - 允许在`const`成员函数中修改类的成员变量[^1][^2][^3]。 - 突破`const`限制,用于修改一些与对象逻辑状态无关的辅助性成员(如缓存、计数器等)[^2][^3]。 **使用场景**: 1. 缓存计算结果(避免重复计算)[^1]: ```cpp class Data { private: mutable std::string cachedResult; // 缓存 bool isCacheValid = false; public: std::string getData() const { if (!isCacheValid) { // 在const函数中更新缓存 cachedResult = expensiveCompute(); // 合法,因为cachedResult是mutable isCacheValid = true; // 错误!isCacheValid不是mutable,不能在const函数中修改 } return cachedResult; } }; ``` 注意:上述代码中`isCacheValid`也需要改为`mutable`才能修改。 2. 调试/性能计数: ```cpp class NetworkConnection { private: mutable int accessCount = 0; // 记录访问次数 public: void sendData(const std::string& data) const { accessCount++; // 合法 // ... 发送数据(逻辑上不改变连接状态) } }; ``` **注意事项**: - `mutable`应谨慎使用,避免破坏`const`的正确性[^2]。 - 通过`const`指针或引用访问对象时,也能修改其`mutable`成员[^3]。 --- ### 对比总结 | 特性 | `volatile` | `mutable` | |---------------|-------------------------------------|------------------------------------| | **目的** | 防止编译器优化,确保内存可见性 | 允许`const`成员函数修改成员变量 | | **适用对象** | 变量(全局、局部、成员) | 类的成员变量 | | **多线程** | 有限支持(不保证原子性) | 非线程安全(需额外同步) | | **使用场景** | 硬件寄存器、信号处理、共享内存 | 缓存、计数器、调试日志 | > **关键区别**: > - `volatile`解决的是**内存访问优化**问题(硬件/多线程场景)。 > - `mutable`解决的是**逻辑常量性**问题(允许修改物理状态但不改变逻辑状态)[^2][^3]。 --- §§相关问题§§ 1. 为什么在多线程编程中`volatile`不能替代原子操作(`std::atomic`)? 2. `mutable`成员变量是否会影响对象的线程安全性? 3. 如何理解C++中的“物理常量性”和“逻辑常量性”?</think>### C++ 中 `volatile` 和 `mutable` 关键字详解 #### 一、`volatile` 关键字 **作用**: 强制编译器每次访问变量时都从内存读取/写入,防止编译器优化导致的意外行为[^1][^2][^4]。 **核心特性**: 1. **禁止缓存优化** 确保每次访问都直接读写内存(而非寄存器缓存) ```cpp volatile int sensorValue; // 硬件寄存器映射 while (sensorValue == 0); // 不会被优化成死循环 ``` 2. **保证操作顺序性** `volatile` 变量间的操作顺序不会被编译器重排[^4]: ```cpp volatile int a, b; a = 1; b = 2; // 编译器保证a赋值先于b ``` 3. **适用场景**: - 硬件寄存器访问(嵌入式开发) - 多线程共享变量(需配合原子操作) - 信号处理程序中的全局变量 **注意事项**: - 不保证原子性(非线程安全) - 与 `const` 可组合:`volatile const int READ_ONLY_REG = 0xFFFF;`[^1] --- #### 二、`mutable` 关键字 **作用**: 允许在 `const` 成员函数或 `const` 对象中修改指定成员变量[^1][^2][^3]。 **核心原理**: ```cpp class DataCache { private: mutable std::string cachedData; // 声明为mutable bool cacheValid = false; public: std::string getData() const { // const成员函数 if (!cacheValid) { cachedData = fetchData(); // 允许修改mutable成员 cacheValid = true; // 错误!非mutable成员不可修改 } return cachedData; } }; ``` **设计意义**: 突破 `const` 限制,修改与对象逻辑状态无关的辅助成员[^2][^3]: - 缓存/计算结果 - 访问计数器 - 调试日志信息 **典型场景**: ```cpp class NetworkConnection { private: mutable int accessCount = 0; // 记录访问次数 public: void send(const std::string& data) const { accessCount++; // 修改mutable成员 // ... 发送逻辑(不改变连接状态) } }; ``` --- #### 三、关键区别对比 | 特性 | `volatile` | `mutable` | |--------------------|-------------------------------------|------------------------------------| | **作用对象** | 任何变量 | 类成员变量 | | **主要目的** | 防止编译器优化 | 突破 `const` 限制 | | **内存访问** | 强制内存读写 | 无特殊影响 | | **多线程安全性** | 需配合原子操作 | 非线程安全 | | **典型使用场景** | 硬件寄存器/信号处理 | 缓存/计数器/调试信息 | > **组合使用示例**: > ```cpp > class Sensor { > private: > mutable volatile int rawValue; // 可被const函数修改的易变值 > public: > int read() const { > return rawValue; // 每次访问都从内存读取 > } > }; > ``` --- ### 总结 1. **`volatile`**: - 解决编译器优化导致的内存访问异常问题 - 硬件编程和多线程的关键工具(需谨慎使用)[^4] 2. **`mutable`**: - 实现物理常量性和逻辑常量性的分离 - 使 `const` 成员函数更灵活(不影响对象逻辑状态)[^3] > 二者均属于底层控制关键字,滥用可能导致代码可读性下降,应严格限定在必要场景[^2]。 ---
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值