不久前,我遇到了一些代码,它们用mutable关键字标记了一个类的成员变量。据我所知,它只是允许你在const方法中修改变量:
class Foo
{
private:
mutable bool done_;
public:
void doSomething() const { ...; done_ = true; }
};
这是唯一的使用这个关键字还是有更多的它比满足眼睛?从那以后,我在一个类中使用了这种技术,将boost::mutex标记为mutable,允许const函数出于线程安全的原因锁定它,但是,说实话,这感觉有点hack。
是的,这就是它的作用。我将它用于那些被方法修改的成员,而这些方法在逻辑上不会改变类的状态——例如,通过实现缓存来加速查找:
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将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。
可变存储类只能用于类的非静态非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的值,编译器会抛出一个错误。