C++ 的关键字 mutable
On 2014-11-19 22:31:45 By SoliC++ 的关键字 mutable
mutable 的特性
mutable 是个很有意思的属性。mutable 声明的类成员可以在 const 方法中被修改;const 实例中的 mutable 成员不受 const 的限制。
见如下实例代码:
class C
{
public:
C():x(1),y(2){}
void func() const // 注意这里的 const
{
x = 3; // error: read-only variable is not assignable
y = 4; // ok
}
int x;
mutable int y;
};
int main()
{
const C c; // 这里也有 const
c.x = 5; // error: read-only variable is not assignable
c.y = 6; // ok
c.func();
return 0;
}
上例中,y
有 mutable 属性,所以它可以在 const 方法 func()
中被修改,并且在具有 const 属性的 c
中也可以被修改。
我们可以这样理解:对 mutable 成员的修改不影响类或实例的 const 状态。
适用场景
那么, mutable 的适用场景有哪些呢?我能想到的有两个。下面通过一个例子把这两种适用场景呈现给大家。
比如,我们有一个容器类 C
,它有两个方法: size()
可以得到当前容器中含有的项目数;capacity()
可以得到当前容器的容量。另外,我们有个函数 status()
,它接收一个 C
的实例作为参数,并通过访问 size()
和 capacity()
把该实例的状态打印出来。我们还有个要求,status()
不会改变 C
实例的内容,所以它的参数应该是 const 的。
代码如下:
class C
{
public:
C():_size(0),_capacity(0){}
int size() const // const 允许 status() 对其访问
{
return _size;
}
int capacity() const // const 允许 status() 对其访问
{
return _capacity;
}
private:
int _size;
int _capacity;
};
void status(const C &c) // const 保证 status 不会改变 c 的内容
{
printf("size=%d, capacity=%d\n", c.size(), c.capacity());
}
上面代码应该是没问题的。下面我们通过加入一些限制来构造我们的适用场景。
适用场景一
先来看 capacity()
。由于某些原因,我们在初始化时并没有初始化成员 _capacity
。我们希望只有当 capacity()
被调用时我们才计算容量的值,并且这个值一旦得到将不再改变。
应该这样实现:
class C
{
//...
int capacity() const
{
if (_capacity == 0)
{
_capacity = ...; // 计算容量值,修改 _capacity
}
return _capacity;
}
private:
int _size;
mutable int _capacity; // 因为需要在 const 方法中被修改,所以必须有 mutable 属性
};
这就是我们的第一个适用场景:当第一次请求时需要计算并缓存,以后只需访问缓存中的值。
适用场景二
再来看 size()
。容器中含有的项目数是时刻在变化的,为了保证线程安全,我们需要在访问 _size
之前加锁。这不难,我们只需在容器 C
中增加个新成员 _lock
即可。但加锁这个动作会改变 _lock
,而对 size()
的 const 声明要求在 size()
中不能修改类成员。这时就需要给 _lock
加 mutable 属性了。
这就是我们的第二个适用场景:const 方法需要修改类成员,而这种修改不需要、也不应该对调用者可见。
实现代码如下:
class C
{
public:
C():_size(0),_capacity(0){}
//...
int size() const
{
int s = 0;
_lock.lock(); // lock() 会修改 _lock
s = _size;
_lock.unlock();
return s;
}
private:
int _size;
mutable Lock _lock; // 因为需要在 const 方法中被修改,所以必须有 mutable 属性
};
C++11 中的 mutable
在 C++11 中,mutable 还会用在 Lambda 表达式中,用来允许执行体修改捕获的参数。Lambda 表达式默认是 const 属性的,故不能修改捕获的参数。
{
int x = 0;
auto l1 = [=]() mutable { x = 1; };
auto l2 = [=]() { x = 2; }; // error: cannot assign to a variable captured by copy in a non-mutable lambda
// ...
}
总结
- mutable 声明的类成员可以在 const 方法中被修改。
- const 实例中的 mutable 成员不受 const 的限制。
- 适用场景一:当第一次请求时需要计算并缓存,以后只需访问缓存中的值。
- 适用场景二:const 方法需要修改类成员,而这种修改不需要、也不应该对调用者可见。
- 在 C++11 中,mutable 还会用在 Lambda 表达式中,用来允许执行体修改捕获的参数。