<1>前言
介绍mutable关键字之前,先了解一下常成员函数:
常成员函数是使用const 关键字声明的类的成员函数:类型说明符 函数名(参数表) const
常成员函数具有一下的特征:
(1)常成员函数可以被常对象调用,也可以被普通对象调用(但常对象只能调用常成员函数,不能调用除构造、析构函数以外的非常成员函数),无论被那种对象调用,在常成员函数中都不能修改对象的数据成员,也不能调用该类中的非常成员函数。(这就保证了在常成员函数中绝对不会更改数据成员的值)
(2)利用常成员函数可以进行函数重载;
void print();
void print() const;
<2>关键字mutable
关键字 mutable 是一个奇怪的修饰符(specifier),它只能够用于一个类的非静态数据成员。下面我将讨论 mutable 的语义和用法,但是首先我要解释一下 C++ 对象模型的一个关键概念。
对象的状态
一个对象的状态由其非静态数据成员的值构成,因此,修改一个数据成员将会改变整个对象的状态。将一个成员函数声明为 const 能够保证它不会改变对象的状态。
然而在一些情况下,对象的逻辑状态与其物理状态之间可能有差别。例如,对于一个表示绘画图像的对象就存在这种情况。如果图像还没有更改,那么我们就认为其状态没有发生变化。然而,从底层实现方面来说,如果大对象在一段时间没有活动,那么它们的内存通常会被交换到一个文件中。交换一个图像并不会真地影响其状态,但是对象的一些数据成员可能会发生变化,在这里可能会发生变化的是指针、标志位等。
在用户调用一个诸如 Redraw() 之类的 const 成员函数时,他们并不关心这个函数在内部是如何实现的。从他们的角度来说,这个函数并不改变对象的逻辑状态,因此被声明为 const。Redraw() 有可能修改对象的物理状态这一事实是一个他们不应该关心的实现细节。例如:
#include<iostream>
using namespace std;
class A
{
public:
A(int a) {num=a;}
void printnum() const
{
num++;
cout<<num<<endl;
}
private:
int num;
};
int main(void)
{
A m(1);
m.printnum();
m.printnum();
return 0;
}
可变(mutable)数据成员
如果尝试编译这段代码,你会得到一个编译错误。虽然printnum()声明为 const,但是它修改了一个数据成员num。解决这个编译错误的方法是将 num声明为一个 mutable 数据成员。不像普通的数据成员, mutable 数据成员可以被const 成员函数修改。
Mutable 数据成员的使用看上去像是骗术,因为它能够使 const 函数修改对象的数据成员。然而,明智地使用 mutable 关键字可以提高代码质量,因为它能够让你向用户隐藏实现细节,而无须使用不确定的东西,比如 const_cast<>。
mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。
在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。
下面是一个小例子:
#include<iostream>
using namespace std;
class A
{
public:
A(int a) {num=a;}
void printnum() const
{
num++;
cout<<num<<endl;
}
private:
<strong>mutable</strong> int num;
};
int main(void)
{
A m(1);
m.printnum();
m.printnum();
return 0;
}
利用mutable前缀修饰数据成员num,此时常成员函数中的num,随着对象每次对成员函数的调用,num的值也会动态更改的。