C++ 是';可变的';关键字除了允许常量函数修改变量外,还有其他用途吗?

C++ 是';可变的';关键字除了允许常量函数修改变量外,还有其他用途吗?,c++,keyword,mutable,C++,Keyword,Mutable,不久前,我遇到了一些代码,它们用mutable关键字标记了类的成员变量。据我所知,它只允许您修改const方法中的变量: class Foo { private: mutable bool done_; public: void doSomething() const { ...; done_ = true; } }; 这是这个关键词的唯一用途,还是它有更多的用途?此后,我在一个类中使用了这种技术,将一个boost::mutex标记为可变的,允许cons

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

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

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

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

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


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

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

那么如果上述疯狂不是什么 易变是用来做什么的?这是 微妙的情况是:mutable用于 对象在逻辑上是 不变,但在实践中需要 改变这些案例非常少 两者之间,但它们存在

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

在某些情况下(如设计拙劣的迭代器),类需要保留一个计数或一些其他附带值,这实际上不会影响类的主要“状态”。这是我经常看到使用可变表的地方。如果没有可变项,您将被迫牺牲设计的全部常量


对我来说,大多数时候这也像是一次黑客攻击。在极少数情况下非常有用。

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

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; }; 类哈希表 { ... 公众: 字符串查找(字符串键)常量 { if(key==lastKey) 返回最后一个值; 字符串值=查找内部(键); lastKey=密钥; lastValue=值; 返回值; } 私人: 可变字符串lastKey、lastValue; };
然后,您可以让
常量哈希表
对象仍然使用其
lookup()
方法,该方法修改内部缓存。

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

由于c++11
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;
};
classciniwrapper
{
公众:
Ciniwraper(LPCTSTR szIniFile);
//非常量:从逻辑上修改对象的状态
void SetValue(LPCTSTR szName,LPCTSTR szValue);
//常量:不在逻辑上更改对象
LPCTSTR GetValue(LPCTSTR szName,LPCTSTR szDefaultValue)const;
// ...
私人:
//缓存,避免在多次检索命名值时转到磁盘
//不在逻辑上更改公共接口,因此声明为可变
//以便const GetValue()方法可以使用它
可变std::map m_mapNameToValue;
};

现在,您必须小心使用它——并发问题是一个很大的问题,因为调用方可能会认为,如果只使用
const
方法,它们是线程安全的。当然,修改
mutable
数据不应该以任何显著的方式改变对象的行为,我给出的示例可能会违反这一点,例如,如果预期写入磁盘的更改将立即对应用程序可见

< P>你使用它不是黑客,尽管C++中的很多东西,对于懒惰的程序员来说,可变的可以是黑客,它不想一路回过头来标记不应该const的非const。

<代码>可更改的< /代码>确实存在,因为你推断允许一个在另一个常量函数中修改数据。 这样做的目的是,您可能有一个对对象的内部状态“不做任何事情”的函数,因此您可以标记函数
const
,但您可能确实需要以不影响其正确功能的方式修改某些对象状态

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

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

  • 线程安全。声明一个
    可变boost::mutex
    是完全合理的
  • 统计数字。给定函数的部分或全部参数,计算函数调用的次数
  • 回忆录。计算一些昂贵的答案,然后将其存储以供将来参考,而不是重新计算

关键字
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;
};
//Prototype 
class tag_name{
                :
                :
                mutable var_name;
                :
                :
               };   
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.
}
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