Does the 'mutable' keyword have any purpose other than allowing the variable to be modified by a const function?
不久前,我遇到了一些用
1 2 3 4 5 6 7 | class Foo { private: mutable bool done_; public: void doSomething() const { ...; done_ = true; } }; |
这是这个关键字的唯一用法,还是它有更多的东西,而不是符合眼睛?后来我在一个类中使用了这种技术,将
它允许区分位常量和逻辑常量。逻辑常量是当一个对象没有以一种通过公共接口可见的方式改变时,就像锁示例一样。另一个例子是一个类,它在第一次被请求时计算一个值,并缓存结果。
因为C++ 11 EDCOX1×1可用于lambda来表示由值捕获的事物是可修改的(它们不是默认的):
1 2 3 | 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 |
使用您的
- 仅对任何可见数据成员进行读取访问
- 只调用标记为
const 的方法的权限。
在内部,那些对您可见的
实际上,您可以在整个代码库中强制使用
如果没有
(注意,我指的是数据和方法可见性几次。我说的是标记为public与private或protected的成员,这是这里讨论的完全不同的对象保护类型。)
使用boost::mutex正是这个关键字的目的。另一个用途是内部结果缓存以加速访问。
基本上,"mutable"适用于任何不影响对象外部可见状态的类属性。
在您所讨论的示例代码中,如果done的值影响外部状态,则mutable可能不合适,这取决于…;部分中的内容。
mutable用于从
网址:http://www.highprogrammer.com/alan/rants/mutable.html
So if the above madness isn't what
mutable is for, what is it for? Here's
the subtle case: mutable is for the
case where an object is logically
constant, but in practice needs to
change. These cases are few and far
between, but they exist.
作者给出的例子包括缓存和临时调试变量。
它在隐藏内部状态(如缓存)的情况下很有用。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class HashTable { ... public: string lookup(string key) const { if(key == lastKey) return lastValue; string value = lookupInternal(key); lastKey = key; lastValue = value; return value; } private: mutable string lastKey, lastValue; }; |
然后你可以让一个
是的,就是这样。我将它用于由不在逻辑上更改类状态的方法修改的成员,例如,通过实现缓存来加快查找速度:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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; }; |
现在,您必须谨慎地使用它——并发性问题是一个很大的问题,因为调用方可能会假设它们是线程安全的,如果只使用
其目的是您可能拥有一个对对象的内部状态"不做任何事情"的函数,因此您标记了函数
关键字可以作为编译器的提示——理论上的编译器可以将一个常量对象(如全局对象)放在标记为只读的内存中。
以下是声明和使用可变数据的一些有效原因:
- 线程安全性。宣布一个
mutable boost::mutex 是完全合理的。 - 统计学。对函数的调用数进行计数,给出函数的部分或全部参数。
- 记忆化。计算一些昂贵的答案,然后将其存储以供将来参考,而不是重新计算。
当类中有一个变量,该变量只在该类中用于表示诸如互斥或锁之类的东西时,就使用mutable。这个变量不会改变类的行为,但对于实现类本身的线程安全是必需的。因此,如果没有"mutable",您将无法拥有"const"函数,因为需要在外部世界可用的所有函数中更改此变量。因此,为了使成员变量可写(即使是通过const函数),引入了可变变量。
The mutable specified informs both the compiler and the reader that it
is safe and expected that a member variable may be modified within a const
member function.
mutable主要用于类的实现细节。类的用户不需要知道它,因此方法的用户认为"应该"是常量。您的mutex是可变的示例是一个很好的规范示例。
你使用它不是黑客,尽管像C++中的很多东西一样,对于懒惰的程序员来说,可变的是可以破解的,他们不想一路回过头来标记不应该成为常量的东西。
对于逻辑上对用户无状态(因此在公共类的API中应该有"const"getter)但在底层实现(在.cpp中的代码)中不是无状态的东西,请使用"mutable"。
我最经常使用它的情况是懒惰地初始化没有状态的"普通老数据"成员。也就是说,在狭隘的情况下,当这些成员构建(处理器)或携带(内存)的成本很高,并且对象的许多用户永远不会要求它们时,这是理想的。在这种情况下,为了提高性能,您需要在后端进行懒惰的构造,因为90%的已构建对象根本不需要构建它们,但是您仍然需要为公共消费提供正确的无状态API。
经典的例子(如其他答案中提到的)和我迄今为止所看到的唯一的情况是缓存一个复杂的
一般来说,使用
这里还有一个详细的解释。
我们使用可变的一个最好的例子是,在深度复制中。在复制构造函数中,我们发送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 |
在上面的示例中,我们可以更改成员变量
当您重写一个常量虚函数并希望修改该函数中的子类成员变量时,可变变量非常方便。在大多数情况下,您不希望更改基类的接口,因此必须使用自己的可变成员变量。
可变关键字在为类测试目的创建存根时非常有用。您可以截取一个const函数,并且仍然能够增加(可变)计数器或您添加到存根中的任何测试功能。这样可以保持存根类的接口完好无损。
在某些情况下(比如设计糟糕的迭代器),类需要保留一个计数或其他一些偶然值,这不会真正影响类的主要"状态"。这是我最常看到的可变使用。如果没有可变的,你就不得不牺牲你设计的整个常量。
对我来说,大部分时间都是一次黑客行为。在极少数情况下有用。
mutable将类的
这意味着具有可变成员的类将不再是位常量,并且不再出现在可执行文件的只读部分中。
此外,它还通过允许
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 | 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. } |
有关更多详细信息,请参阅其他答案,但我想强调的是,它不仅适用于类型安全,而且会影响编译结果。
关键字"mutable"实际上是一个保留关键字。它通常用于更改常量变量的值。如果要有一个constsnt的多个值,请使用关键字mutable。
1 2 3 4 5 6 7 8 | //Prototype class tag_name{ : : mutable var_name; : : }; |