除了允许variables被const函数修改以外,'mutable'关键字还有其他用途吗?

前一段时间我遇到了一些代码,用mutable关键字标记了一个类的成员variables。 据我可以看到,它只是允许你修改一个const方法的variables:

 class Foo { private: mutable bool done_; public: void doSomething() const { ...; done_ = true; } }; 

这是这个关键字的唯一用途,还是有更多的比它满足眼睛? 我已经在一个类中使用了这个技术,将一个boost::mutex标记为mutable,允许const函数为了线程安全的原因locking它,但是说实话,这感觉就像是一个黑客。

它允许按位const和逻辑const的区分。 逻辑常量是指当一个对象没有通过公共接口可见的方式改变时,就像你的locking例子。 另一个例子是在第一次请求时计算一个值的类,并caching结果。

由于c ++ 11 mutable可以在lambdaexpression式中使用,表示通过值捕获的东西是可以修改的(它们不是默认的):

 int x = 0; auto f1 = [=]() mutable {x = 42;}; // OK auto f2 = [=]() {x = 42;}; // Error: a by-value capture cannot be modified in a non-mutable lambda 

mutable关键字是一种穿透您遮盖物体的const的方法。 如果您有一个const引用或指向对象的指针, 除了何时以及如何将其标记为mutable 之外 ,不能以任何方式修改该对象。

用你的const引用或指针,你被限制在:

  • 只读取任何可见的数据成员的访问权限
  • 只能调用标记为const方法的权限。

mutableexception使得它现在可以写或设置标记为mutable数据成员。 这是唯一的外部可见的差异。

在内部,你可以看到的const方法也可以写入标记为mutable数据成员。 从本质上来说,综合刺破了常规的面纱。 这完全取决于APIdevise者,以确保mutable不会破坏const概念,只在有用的特殊情况下使用。 mutable关键字有帮助,因为它清楚地标记了受这些特殊情况影响的数据成员。

在实践中,你可以在整个代码库中使用const (你本质上是想用const “疾病”来感染你的代码库)。 在这个世界中,指针和引用是const ,极less有例外,产生的代码更容易推理和理解。 对于一个有趣的离题,请查阅“参考透明度”。

没有mutable关键字,你最终将被迫使用const_cast来处理它允许的各种有用的特殊情况(caching,参考计数,debugging数据等)。 不幸的是, const_castmutable具有更强的破坏性,因为它迫使API 客户端销毁他正在使用的对象的const保护。 此外,它会导致广泛的const破坏: const_cast一个const指针或引用允许不受限制的写和方法调用访问可见成员。 相比之下, mutable需要APIdevise者对constexception进行细粒度的控制,通常这些exception隐藏在对私有数据进行操作的const方法中。

(注:我指的是数据和方法的几次可见性,我指的是标记为公共或私有或被保护的成员,这是一个完全不同的对象保护types。

你使用boost :: mutex正是这个关键字的目的。 另一个用途是内部结果caching来加速访问。

基本上,“可变”适用于任何不影响对象的外部可见状态的类属性。

在你的问题的示例代码中,如果done_的值影响外部状态,mutable可能不合适,这取决于…中的内容; 部分。

可变是将特定属性标记为const方法中可修改的。 这是它唯一的目的。 在使用之前要仔细考虑,因为如果你改变devise而不是使用mutable话,你的代码可能会更干净和更具可读性。

http://www.highprogrammer.com/alan/rants/mutable.html

所以如果上面的疯狂不是可变的,那是什么呢? 下面是一个微妙的例子:mutable是指对象在逻辑上是常量的情况,但实际上需要改变。 这些情况很less,而且存在。

作者给出的例子包括caching和临时debuggingvariables。

在隐藏内部状态(如caching)的情况下,它非常有用。 例如:

 HashTable类
 {
 ...
上市:
    string查找(string键)常量
     {
        如果(key == lastKey)
            返回lastValue;

        string值= lookupInternal(key);

         lastKey = key;
         lastValue = value;

        返回值;
     }

私人的:
    可变stringlastKey,lastValue;
 }; 

然后你可以有一个const HashTable对象仍然使用它的lookup()方法,它修改了内部caching。

那么,是的,就是这样。 我用它来修改那些不会在逻辑上改变类的状态的方法所修改的成员,例如,通过实现一个caching来加速查找:

 class CIniWrapper { public: CIniWrapper(LPCTSTR szIniFile); // non-const: logically modifies the state of the object void SetValue(LPCTSTR szName, LPCTSTR szValue); // const: does not logically change the object LPCTSTR GetValue(LPCTSTR szName, LPCTSTR szDefaultValue) const; // ... private: // cache, avoids going to disk when a named value is retrieved multiple times // does not logically change the public interface, so declared mutable // so that it can be used by the const GetValue() method mutable std::map<string, string> m_mapNameToValue; }; 

现在,你必须小心使用它 – 并发性问题是一个大问题,因为调用者可能会认为它们是线程安全的,如果只使用const方法。 当然,修改mutable数据不应该以任何重要的方式改变对象的行为,例如,如果预期写入磁盘的改变将立即可见应用程序。

mutable确实存在,因为您推断允许修改其他常数函数中的数据。

意图是你可能有一个函数“对对象的内部状态”什么都不做“,所以你标记了函数const ,但是你可能真的需要修改一些对象的状态,而不会影响它的状态正确的function。

关键字可以作为编译器的提示 – 一个理论编译器可以在一个标记为只读的内存中放置一个常量对象(如全局对象)。 mutable提示的存在,这不应该做的。

这里有一些正确的理由来声明和使用可变数据:

  • 线程安全。 声明一个mutable boost::mutex是完全合理的。
  • 统计。 计算一个函数的调用次数,并给出它的部分或全部参数。
  • 记忆化。 计算一些昂贵的答案,然后将其存储起来供将来参考,而不是再次重新计算。

mutable主要用于类的实现细节。 类的用户不需要知道它,所以方法的他认为“应该”是const可以的。 你有一个互斥的例子是可变的是一个很好的例子。

你对它的使用并不是黑客行为,尽pipe像C ++中的许多事情一样,mutable对于懒惰的程序员来说是不可行的,因为懒惰的程序员不希望一路走回头路,并且标记不应该是非const的const。

当类内部有一个variables时,就会使用Mutable,该variables只在该类中用于表示例如互斥锁或锁的信号。 这个variables不会改变类的行为,但是为了实现类本身的线程安全是必须的。 因此,如果没有“可变”,你将无法拥有“常量”function,因为这个variables将需要在外部世界可用的所有function中改变。 因此,为了使成员variables甚至通过const函数可写,引入了mutable。

指定的mutable通知编译器和读者它是安全的,并且期望成员variables可以在const成员函数内被修改。

对于逻辑无状态的用户(因此在公共类的API中应该有“const”getters),但在底层IMPLEMENTATION(.cpp中的代码)中不是无状态的时,使用“mutable”。

我最经常使用的情况是无状态的“普通旧数据”成员的延迟初始化。 也就是说,对于构build(处理器)或随身携带(记忆)这样的成员是昂贵的,这种狭义的情况是理想的,并且对象的许多用户将永远不会要求他们。 在这种情况下,你需要在后端进行惰性构build,因为90%的对象根本就不需要构build它们,但是仍然需要为公共消费呈现正确的无状态API。

在一些情况下(比如devise不好的迭代器),类需要保留一个计数或者其他一些附带值,这并不影响类的主要“状态”。 这是最常见的地方,我看到可变使用。 没有可变的,你将被迫牺牲你的devise的整个一致性。

大部分时间对我来说,感觉就像是黑客。 在非常less的情况下有用。

经典的例子(正如其他答案中所提到的)以及我所见过的关于mutable关键字的唯一情况是用于caching复杂Get方法的结果,其中caching被实现为类的数据成员,而不是作为该方法中的一个静态variables(出于几个function之间的共享或普通清洁的原因)。

通常,使用mutable关键字的替代方法通常是方法或const_cast技巧中的静态variables。

另一个详细的解释是在这里 。

为类testing目的创build存根时,mutable关键字非常有用。 您可以存根const函数,仍然可以增加(可变)计数器或任何testingfunction,你已经添加到您的存根(stub)。 这使被保留的类的接口保持不变。

Mutable将const的含义从类的const逐个变为逻辑的const。

这意味着具有可变成员的类更长时间是按位const,并且不会再出现在可执行文件的只读部分。

此外,它通过允许const成员函数更改可变成员而不使用const_cast来修改types检查。

 class Logical { mutable int var; public: Logical(): var(0) {} void set(int x) const { var = x; } }; class Bitwise { int var; public: Bitwise(): var(0) {} void set(int x) const { const_cast<Bitwise*>(this)->var = x; } }; const Logical logical; // Not put in read-only. const Bitwise bitwise; // Likely put in read-only. int main(void) { logical.set(5); // Well defined. bitwise.set(5); // Undefined. } 

有关更多详细信息,请参阅其他答案,但是我想强调一下,这不仅仅适用于types安全,而且会影响编译结果。

当你重写一个const虚函数并且想要在那个函数中修改你的子类成员variables的时候,mutable可能会很方便。 在大多数情况下,你不想改变基类的接口,所以你必须使用你自己的可变成员variables。

关键字'mutable'实际上是一个保留关键字。常用于改变常量variables的值。如果你想有一个constsnt的多个值,使用关键字mutable。

 //Prototype class tag_name{ : : mutable var_name; : : }; 

我们使用mutable的最好例子之一就是深拷贝。 在复制构造函数中,我们发送const &obj作为参数。 所以创build的新对象将是常量types。 如果我们想改变(大多数情况下我们不会改变,在极less情况下我们可能会改变),这个新创build的const对象中的成员,我们需要声明它是mutable

mutable存储类只能用于类的非静态非常量数据成员。 即使它是声明为const的对象的一部分,也可以修改类的可变数据成员。

 class Test { public: Test(): x(1), y(1) {}; mutable int x; int y; }; int main() { const Test object; object.x = 123; //object.y = 123; /* * The above line if uncommented, will create compilation error. */ cout<< "X:"<< object.x << ", Y:" << object.y; return 0; } Output:- X:123, Y:1 

在上面的例子中,我们可以改变成员variablesx的值,尽pipe它是一个声明为const的对象的一部分。 这是因为variablesx被声明为可变的。 但是如果你试图修改成员variablesy的值,编译器会抛出一个错误。