• C++中mutable关键字存在的必要性是什么? - 朋克李PunkLi的回答 - 知乎 https://www.zhihu.com/question/64969053/answer/226195203
  • C++中mutable关键字存在的必要性是什么? - 知乎 https://www.zhihu.com/question/64969053/answer/226169333

未完成

因为我看不懂 《Effective Modern C++》 的 Item 16 部分。

为什么 mutex 只有 move , 没有 copy 。 所以本章还没有完成。

逻辑上 Constness

《Effective C++》 条款3 说明了,某些函数是 “Logical Constness” 而不是 “Bitwise Constness” 。

说白了就是不允许大幅度地修改对象内容,但允许某些变量被修改

常见的通途 (由于 mutable 是被隐藏的,所以下面三种用途非常安全)

  • access counter 计算某个对象的访问次数
  • cache 缓存
  • mutex 线程安全

Const Member Function 的隐患

https://randoruf.github.io/2021/08/15/cpp-const.html 总结的一模一样,只不过那时候我还不懂 Bitwise Constness 。

所谓的 Bitwise Constness 就是 不会修改 this 对象的内容

那么问题来了, 成员指针指的对象算不算 this 对象的内容

答案:不算。因为编译器说不算。编译器只能保证指针num 的地址不变

class A{
public:
    int* num;
    A(){ num = new int(10); }
    ~A() { delete num; } 

    void setNum(int n) const{
        *num = n; 
    }
}; 

缓存

https://github.com/kelthuzadx/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item16.md

例如 “多项式求根” 本身不会改变多项式本身 (logical costness) ,但是多项式求根是非常消耗资源的。

所以最好缓存起来。

class Polynomial {
public:
    using RootsType = std::vector<double>;
    
    RootsType roots() const{
        if (!rootsAreValid) {               //如果缓存不可用
             //计算根
            //用rootVals存储它们
            rootsAreValid = true;
        }
        
        return rootVals;
    }
    
private:
    mutable bool rootsAreValid{ false };    //初始化器(initializer)的
    mutable RootsType rootVals{};           //更多信息请查看条款7
};

线程安全

上面的例子在多线程情况下,会有 Race Condition

  • 两个进程都尝试修改同一个值,有时候会被覆盖。
  • 所以需要 mutex 来保证这一个对象的互斥访问 (仅仅针对这一个对象)
  • 注意不能用 static 来声明 mutex ,因为你会有很多不同的多项式,例如 \(x^2 + x\) 和 $x^3$ 是不一样的。

依旧保留 const 的情况下 (希望有 Logical Constness) ,

虽然 mutex 确实不会改变多项式本身,但会改变对象的 mutex

class Polynomial {
public:
    using RootsType = std::vector<double>;
    
    RootsType roots() const{
        std::lock_guard<std::mutex> g(m);       //锁定互斥量
       
        if (!rootsAreValid) {                   //如果缓存无效
                                               //计算/存储根值
            rootsAreValid = true;
        }
        
        return rootsVals;
    }                                           //解锁互斥量
    
private:
    mutable std::mutex m;
    mutable bool rootsAreValid { false };
    mutable RootsType rootsVals {};
};

Access Counter

看看函数被使用了多少次。

class BigArray {
    vector<int> v; 
    int accessCounter;
public:
    int getItem(int index) const { 
        accessCounter++;
        return v[index];
    }
};