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++11mutable
可以在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