关于c ++:过度使用mutable来增强安全性?

Over-using mutable to enhance security?

假设我有一个具有指针数组的类,我有一个方法取消引用指针并将其作为引用返回。 我想允许方法调用者调用指针指向的对象的非const方法,但也希望保护自己免受调用者更改指针所指向的内容。 如果我返回一个const引用,我必须将许多指针对象的方法标记为const,因此它的许多类成员变量都是可变的。

  • 这是不好的做法吗? 如果是这样,我该如何解决这个问题呢?
  • 过度使用可变性是否存在性能损失?
  • 例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    #include <iostream>
    #include
    #include <memory>

    class Counter
    {
    public:
      Counter();
      void hit() const;
      void reset();
      unsigned count() const;
    private:
      mutable unsigned count_;
    };

    Counter::Counter() : count_(0) {}

    void Counter::hit() const { ++count_; }

    void Counter::reset() { count_ = 0; }

    unsigned Counter::count() const { return count_; }

    class CircularArray
    {
    public:
      CircularArray();
      const Counter& next() const;
    private:
      mutable unsigned i_;
      std::array<std::unique_ptr<Counter>, 3> arr_;
    };

    CircularArray::CircularArray() : i_(2)
    {
      arr_[0] = std::unique_ptr<Counter>(new Counter);
      arr_[1] = std::unique_ptr<Counter>(new Counter);
      arr_[2] = std::unique_ptr<Counter>(new Counter);
    }

    const Counter& CircularArray::next() const { return *arr_[(i_ = (i_ + 1) % 3)]; }

    int main()
    {
      CircularArray circular;
      const Counter* p;
      p = &circular.next();

      p->hit();
      p->hit();

      Counter c;
      //*p = c; // <-- Want to prevent this
    }


    为了扩展我所说的内容,滥用mutable是没有意义的。 如果这是你想要阻止的全部:

    1
    *p = /* ... */;

    然后通过删除Counter的赋值运算符可以更轻松地完成它:

    1
    2
    3
    4
    5
    class Counter
    {
        void operator=(const Counter&) = delete;
        // ...
    };

    请记住,赋值运算符不会影响对象的标识:它不会更改其地址。 语义上,涉及修改this对象以复制另一个对象的状态的赋值。 事实上,即使你禁止我以某种方式使用赋值运算符,我仍然可以这样做:

    1
    2
    3
    4
    // a very inefficient way of performing `*p = c`
    p->reset();
    while (p->count() != c.count())
        p->hit();

    这实现了与执行分配完全相同的结果,尽管非常笨拙且效率低下。

    执行赋值与调用接受类型const Counter&的单个参数的非const成员函数没有什么不同。 假设您可以重新定义赋值运算符,如果您愿意,则根本不做任何事情(尽管这可能是一个坏主意)。