'可变'关键字除了允许变量被const函数修改之外还有其他用途吗?

不久前,我遇到了一些代码,它们用mutable关键字标记了一个类的成员变量。据我所知,它只是允许你在const方法中修改一个变量:

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

这是唯一的使用这个关键字还是有更多的它比满足眼睛?此后我在一个类中使用了这种技术,将boost::mutex标记为mutable,允许const函数出于线程安全的原因锁定它,但是,说实话,这感觉有点hack。

265995 次浏览

使用boost::mutex正是这个关键字的目的。另一个用途是内部结果缓存,以加快访问速度。

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

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

Mutable用于在const方法中标记特定属性为可修改的。这是它唯一的目的。在使用它之前要仔细考虑,因为如果您更改设计而不是使用mutable,那么您的代码可能会更干净、更可读。

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

所以如果上面的疯狂不是什么 可变是用来干什么的?这是 微妙的情况是:mutable是用于 对象在逻辑上的情况 不变,但在实践中需要 改变。这样的案例少而又远

.

.

作者给出的例子包括缓存和临时调试变量。

在某些情况下(比如设计糟糕的迭代器),类需要保留一个计数或其他一些附带的值,这并不真正影响类的主要“状态”。这是我最常看到使用mutable的地方。如果没有mutable,您将被迫牺牲设计的整个const-ness。

对我来说,大多数时候这也像个黑客。在非常非常少的情况下有用。

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

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;
};

然后,您可以让const HashTable对象仍然使用它的lookup()方法,该方法修改内部缓存。

它允许区分按位的const和逻辑的const。逻辑const是指对象没有以通过公共接口可见的方式更改,就像锁定示例一样。另一个例子是一个类,它在第一次请求时计算一个值,并缓存结果。

由于c++11 mutable可以用在lambda上,表示通过值捕获的东西是可修改的(默认情况下不是):

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的方法可以是。让互斥量可变的例子是一个很好的规范示例。

是的,这就是它的作用。我将它用于被不在逻辑上改变类状态的方法修改的成员-例如,通过实现缓存来加快查找:

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数据不应该以任何重要的方式改变对象的行为,如果我给出的例子可能会违反这一点,例如,它期望写入磁盘的更改将立即对应用程序可见。

你对它的使用并不是一种hack,尽管像c++中的许多东西一样,mutable 可以对于一个懒惰的程序员来说是一种hack,他们不想回溯到所有的方法并将不应该是const的东西标记为非const。

mutable确实存在,因为您可以推断允许在一个常量函数中修改数据。

这样做的目的是,您可能有一个函数对对象的内部状态“什么都不做”,因此您将函数标记为const,但是您可能确实需要修改一些对象状态,以不影响其正确功能的方式。

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

下面是声明和使用可变数据的一些有效理由:

  • 线程安全。声明mutable boost::mutex是完全合理的。
  • 统计数据。给定函数的部分或全部实参,计算函数的调用次数。
  • 记忆。计算一些代价高昂的答案,然后将其存储起来以备将来参考,而不是重新计算。

mutable关键字是一种方法来刺穿const面纱你覆盖在你的对象。如果你有一个指向对象的const引用或指针,当它被标记为时,你不能以任何方式修改该对象。

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

  • 仅对任何可见数据成员进行读访问
  • 允许只调用标记为const的方法。

mutable异常使得您现在可以编写或设置标记为mutable的数据成员。这是唯一能从外部看到的区别。

在内部,那些对您可见的const方法也可以写入标记为mutable的数据成员。本质上,const面纱是全面穿透的。API设计人员完全负责确保mutable不会破坏const概念,并且只在有用的特殊情况下使用。mutable关键字很有帮助,因为它清楚地标记了受这些特殊情况影响的数据成员。

在实践中,您可以在代码库中频繁地使用const(您实际上希望用const“疾病”“感染”代码库)。在这个世界中,指针和引用都是const,很少有例外,从而产生更容易推理和理解的代码。一个有趣的题外话,请查阅“参考透明度”。

如果没有mutable关键字,您最终将被迫使用const_cast来处理它允许的各种有用的特殊情况(缓存、引用计数、调试数据等)。不幸的是,const_castmutable更具破坏性,因为它迫使API const_cast0破坏他正在使用的对象的const保护。此外,它还会导致广泛的const破坏:const_casting const指针或引用允许不受约束的写和方法调用对可见成员的访问。相反,mutable要求API设计者对const异常进行细粒度控制,通常这些异常隐藏在对私有数据进行操作的const方法中。

(注意:我多次引用数据和方法可见性。我说的是标记为public、private或protected的成员,这是一种完全不同的对象保护类型。

经典的例子(在其他回答中提到),也是我迄今为止看到的使用mutable关键字的唯一情况,是用于缓存复杂的Get方法的结果,其中缓存是作为类的数据成员实现的,而不是作为方法中的静态变量(出于几个函数之间的共享或简单的清洁)。

一般来说,使用mutable关键字的替代方法通常是方法中的静态变量或const_cast技巧。

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

当你在类中有一个变量,只在类中使用它来标记诸如互斥或锁之类的东西时,就会使用Mutable。这个变量不会改变类的行为,但是为了实现类本身的线程安全,这个变量是必要的。因此,如果没有“mutable”,你将无法拥有“const”函数,因为这个变量将需要在所有对外界可用的函数中被更改。因此,引入mutable是为了使成员变量甚至可以被const函数写。

指定的可变变量通知编译器和读取器 成员变量可以在const中修改,这是安全和预期的吗 成员函数。< / p >

对于那些对用户来说是逻辑上无状态的东西(因此在公共类的api中应该有“const”getter),但在底层实现(你的.cpp中的代码)中不是无状态的东西,使用“mutable”。

我最常使用它的情况是惰性初始化无状态的“普通旧数据”成员。也就是说,当这些成员的构建(处理器)或携带(内存)成本很高,并且对象的许多用户永远不会要求它们时,它是理想的。在这种情况下,为了提高性能,您希望在后端进行惰性构造,因为构建的90%的对象根本不需要构建它们,但是您仍然需要为公共使用提供正确的无状态API。

当你重写一个const虚函数并想要修改该函数中的子类成员变量时,mutable可以很方便。在大多数情况下,您不希望更改基类的接口,因此必须使用自己的可变成员变量。

关键字'mutable'实际上是一个保留关键字。它通常用来改变常数变量的值。如果你想要一个常量的多个值,使用关键字mutable。

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

在为类测试目的创建存根时,mutable关键字非常有用。你可以存根一个const函数,仍然能够增加(可变的)计数器或任何你已经添加到存根的测试功能。这使存根类的接口保持不变。

Mutable将const的含义从按位的const更改为类的逻辑const。

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

此外,它通过允许const成员函数在不使用const_cast的情况下更改可变成员来修改类型检查。

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.
}

请参阅其他答案了解更多细节,但我想强调的是,这不仅仅是为了类型安全,而且它会影响编译结果。

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

mutable存储类只能用于类的非静态非const数据成员。类的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

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