575

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

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

这是这个关键字的唯一用途,还是有更多的用处?从那以后,我在一个类中使用了这种技术,将 a 标记boost::mutex为可变的,允许const函数出于线程安全的原因锁定它,但是,老实说,这感觉有点像 hack。

4

18 回答 18

393

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

由于 c++11mutable可以在 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
于 2008-09-19T20:04:14.100 回答
155

mutable关键字是一种穿透const覆盖在物体上的面纱的方法。如果您有一个指向对象的 const 引用或指针,则您不能以任何方式修改该对象,除非它被标记的时间和方式mutable

使用您的const参考或指针,您将被限制为:

  • 仅对任何可见数据成员的读取权限
  • 仅调用标记为 的方法的权限const

mutable例外使您现在可以编写或设置标记为 的数据成员mutable。这是唯一外部可见的区别。

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

在实践中,您可以const在整个代码库中痴迷地使用(您本质上是想用const“疾病”“感染”您的代码库)。在这个世界中,指针和引用const几乎没有例外,产生的代码更容易推理和理解。对于一个有趣的题外话查找“参考透明度”。

如果没有mutable关键字,您最终将被迫使用它const_cast来处理它允许的各种有用的特殊情况(缓存、引用计数、调试数据等)。不幸const_cast的是,它的破坏性比mutable它强制 API客户端破坏const他正在使用的对象的保护要大得多。此外,它会导致广泛的const破坏:const_cast使用 const 指针或引用允许对可见成员进行不受限制的写入和方法调用访问。相比之下mutable,API 设计人员需要对异常进行细粒度控制const,而这些异常通常隐藏在const对私有数据进行操作的方法中。

(注意,我多次提到数据和方法的可见性。我说的是标记为公共与私有或受保护的成员,这是此处讨论的完全不同类型的对象保护。)

于 2010-03-05T02:34:00.533 回答
78

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

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

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

于 2008-09-19T20:01:20.223 回答
37

Mutable 用于将特定属性标记为可在const方法内修改。这是它的唯一目的。在使用它之前请仔细考虑,因为如果您更改设计而不是使用mutable.

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

因此,如果上述疯狂不是 mutable 的用途,那它的用途是什么?这是一个微妙的情况:可变对象适用于对象在逻辑上是恒定的,但实际上需要更改的情况。这些情况很少见,但它们确实存在。

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

于 2008-09-19T20:02:55.947 回答
36

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

类哈希表
{
...
上市:
    字符串查找(字符串键)常量
    {
        如果(键 == 最后一个键)
            返回最后一个值;

        字符串值 = 查找内部(键);

        最后一个键 = 键;
        最后一个值 = 值;

        返回值;
    }

私人的:
    可变字符串 lastKey, lastValue;
};

然后你可以让一个const HashTable对象仍然使用它的lookup()方法,这会修改内部缓存。

于 2008-09-19T20:03:13.790 回答
10

mutable正如您推断的那样,确实存在以允许人们在其他恒定函数中修改数据。

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

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

以下是声明和使用可变数据的一些正当理由:

  • 线程安全。声明 amutable boost::mutex是完全合理的。
  • 统计数据。在给定部分或全部参数的情况下,计算对函数的调用次数。
  • 记忆。计算一些昂贵的答案,然后将其存储以供将来参考,而不是再次重新计算。
于 2008-09-19T20:25:03.350 回答
8

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

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

于 2008-09-19T20:07:14.280 回答
7

当您在类中有一个变量时使用可变的,该变量仅在该类中用于表示诸如互斥锁或锁之类的东西。这个变量不会改变类的行为,但是为了实现类本身的线程安全是必需的。因此,如果没有“可变”,您将无法拥有“常量”函数,因为需要在外部世界可用的所有函数中更改此变量。因此,引入 mutable 是为了使成员变量即使是 const 函数也可写。

指定的 mutable 通知编译器和阅读器它是安全的,并且可以预期成员变量可以在 const 成员函数中修改。

于 2013-06-28T12:31:09.630 回答
7

对于对用户来说逻辑上无状态的事物(因此在公共类的 API 中应该有“const”getter)但在底层实现(.cpp 中的代码)中不是无状态的,请使用“可变”。

我最常使用它的情况是无状态“普通旧数据”成员的延迟初始化。即,在此类成员的构建(处理器)或携带(内存)昂贵且对象的许多用户永远不会要求它们的狭窄情况下,这是理想的。在这种情况下,您需要在后端进行惰性构建以提高性能,因为 90% 的构建对象根本不需要构建它们,但您仍然需要提供正确的无状态 API 以供公众使用。

于 2014-01-14T02:38:37.357 回答
5

mutable 主要用于类的实现细节。该类的用户不需要知道它,因此他认为“应该”是 const 的方法可以。您让互斥体可变的示例是一个很好的规范示例。

于 2008-09-19T20:05:22.977 回答
5

您对它的使用不是 hack,尽管就像 C++ 中的许多东西一样,对于不想一路返回并将不应该是 const 的东西标记为 non-const 的懒惰程序员来说,mutable可能是 hack。

于 2008-09-19T20:20:49.630 回答
2

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

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

于 2014-07-24T19:50:19.040 回答
1

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

对我来说,大部分时间也感觉像是一种黑客行为。在极少数情况下很有用。

于 2008-09-19T20:03:04.573 回答
1

mutable经典示例(如其他答案中所述)和迄今为止我看到的唯一情况是用于缓存复杂Get方法的结果,其中缓存被实现为类的数据成员而不是作为方法中的静态变量(出于在多个函数之间共享或简单清洁的原因)。

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

另一个详细的解释在这里

于 2011-05-16T12:32:31.973 回答
1

当您覆盖 const 虚函数并想要修改该函数中的子类成员变量时,可变变量会很方便。在大多数情况下,您不想更改基类的接口,因此您必须使用自己的可变成员变量。

于 2014-02-01T14:15:16.893 回答
1

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

于 2014-05-27T08:06:05.987 回答
1

我们使用 mutable 的最佳示例之一是深拷贝。在复制构造函数中,我们const &obj作为参数发送。所以创建的新对象将是常量类型。如果我们想要更改(大多数情况下我们不会更改,在极少数情况下我们可能会更改)这个新创建的 const 对象中的成员,我们需要将其声明为mutable.

mutable存储类只能用于类的非静态非 const 数据成员。可以修改类的可变数据成员,即使它是声明为 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,编译器会抛出错误。

于 2015-01-25T18:19:03.073 回答
-4

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

//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   
于 2014-02-22T03:21:34.527 回答