省时摘要:
mutable 和 const 是 反义词,const 用来标记对象不可修改 而mutable用来标记在const的对象里面的可修改成员。 而作为关键词字的volatile 其实是用来保证多线程功能正确的。
具体实践:
mutable, const 和 volatile 都是C++的关键字,在 cppreference 里面被归类到了 cv-type qualifiers 里面。
实际上阅读下来,似乎这个 C/V中的 volatile 是语义上的可变而不是作为keyword 的 volatile(易变)。 以下分别记录:
Const
const用来标记对象不可修改,这个限定词主要用来保证代码的质量。 在绝大多数地方,被const修饰的对象,都可以去掉const而保证功能正常,const主要用来避免各种误操作。 Effective C++中建议我们对于const 应该以应使用尽量使用的原则积极使用,可以避免对象内容被误修改而引发的潜在bug。比如传参的时候尽量使用 const Obj&的形式。
善用const 以提高代码质量算是C++程序员的自我修养的一部分。
Mutable
基于对const 的理解,我们需要考虑一个class 可能被实例化成const的情况。如果我们有特殊的需求,需要保证尽管这个class 被声明为const,它的某些成员变量仍然需要被修改,这个时候我们就需要使用mutable关键字了。
这个关键字是const 的反义词,但不是在刹车上抹油那么简单。它的存在是为了避免因为有少数的必须修改的成员变量而导致整个类都无法声明为const的尴尬。换而言之,是为了const可以应用的更广泛。
一个简单的 的例子。
#include <iostream>
class X
{
public:
bool GetFlag() const
{
m_accessCount++;
std::cout << "access Count is: " << m_accessCount << std::endl;
return m_flag;
}
private:
bool m_flag = false;
mutable int m_accessCount;
};
int main()
{
const X cx = X();
std::cout << "Get flag of a const object." << std::endl;
cx.GetFlag();
std::cout << "Get flag of a const object again." << std::endl;
cx.GetFlag();
}
运行结果如下:
我们可以看到尽管X被声明为const,它的成员变量m_accessCount 仍然被更新了。
如果 去掉 成员变量 m_accessCount 的 mutable 限定,在VS2022上会报编译错误 C3490. 这个是期待行为,如果没有mutable的特殊声明,当一个对象被声明为const时我们就默认它不应该被修改,compiler在编译期提前帮我们避免了潜在的bug。
Volatile:
volatile关键词其实和mutable 以及const不是一个概念,不知道cppreference 为啥给放在了一起。
volatile 作为关键字主要用来标记一个变量不可以放入高速缓存。
这主要是出于多线程的考虑,目前的多核CPU,一般都是私有的L1,L2 缓存,即每个核心有自己的L1,L2缓存,互相不可以交叉访问。如果变量被一个核心修改并放在了自己的高速缓存里面,如果另一个线程通过另一个核心恰好也在使用这个变量,就没法获取这个变量的最新值,会出现数据不同步的问题。 这个标记指示编译器对程序进行优化的时候不要把某个变量放在这种高速缓存中,而是应该放在内存或者共享的L3缓存中(L3 存疑,个人猜测)。
因此Volatile限定符可以和const/mutable 同时修饰一个变量,哪怕是volatile const这种语义上没啥意义的情况(const不可修改,不改就不会有不同步问题),编译器也不会报错。