Chinaunix首页 | 论坛 | 博客
  • 博客访问: 107507
  • 博文数量: 94
  • 博客积分: 2245
  • 博客等级: 大尉
  • 技术积分: 613
  • 用 户 组: 普通用户
  • 注册时间: 2012-01-26 16:35
文章分类

全部博文(94)

文章存档

2013年(8)

2012年(86)

我的朋友

分类: C/C++

2012-09-05 20:00:55

深入理解C++中的mutable关键字

 mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。

  在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。

  我们知道,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutalbe来修饰。

  下面是一个小例子:

class ClxTest
{
 public:
  void Output() const;
};

void ClxTest::Output() const
{
 cout << "Output for test!" << endl;
}

void OutputTest(const ClxTest& lx)
{
 lx.Output();
}

  类ClxTest的成员函数Output是用来输出的,不会修改类的状态,所以被声明为const的。

  函数OutputTest也是用来输出的,里面调用了对象lx的Output输出方法,为了防止在函数中调用其他成员函数修改任何成员变量,所以参数也被const修饰。

   如果现在,我们要增添一个功能:计算每个对象的输出次数。如果用来计数的变量是普通的变量的话,那么在const成员函数Output里面是不能修改该 变量的值的;而该变量跟对象的状态无关,所以应该为了修改该变量而去掉Output的const属性。这个时候,就该我们的mutable出场了——只要 用mutalbe来修饰这个变量,所有问题就迎刃而解了。

  下面是修改过的代码:

class ClxTest
{
 public:
  ClxTest();
  ~ClxTest();

  void Output() const;
  int GetOutputTimes() const;

 private:
  mutable int m_iTimes;
};

ClxTest::ClxTest()
{
 m_iTimes = 0;
}

ClxTest::~ClxTest()
{}

void ClxTest::Output() const
{
 cout << "Output for test!" << endl;
 m_iTimes++;
}

int ClxTest::GetOutputTimes() const
{
 return m_iTimes;
}

void OutputTest(const ClxTest& lx)
{
 cout << lx.GetOutputTimes() << endl;
 lx.Output();
 cout << lx.GetOutputTimes() << endl;
}

  计数器m_iTimes被mutable修饰,那么它就可以突破const的限制,在被const修饰的函数里面也能被修改。

-------------------------------简朴的分割线----------------------------------


我们知道在用const修饰的类成员函数,这意味着什么,从effective c++这本书上我们可以了解到,这里存在两个流行的概念:bitwise constness(or physical constness)和logical constness.

bitwise const阵营的人相信,成员函数只有在不更改对象任何成员变量(static变量除外)时才可以说是const.也就是说它不能更改对象内的任何一个bit.然而我们不难发现有些例子能够通过bitwise测试却存在潜在的改变成员变量的危险,比如说:

点击(此处)折叠或打开

  1. class String{
  2. public:
  3.       String(char* pstring):pstring_(pstring)
  4.       {
  5.       }
  6.       char& operator[](int index)const
  7.       {
  8.             ...
  9.             return pstring_[index];
  10.       }
  11. private:
  12.       char* pstring_;
  13. };

客户可以写下如下代码:
const String str("any man of mine!");
char& c=str[0]; //call const-function
c='B'; //modified 'pstring_ ' from outer

这样写很显然不符合const的基本出发点(不能改变对象的属性),但是operator[]是通过bitwise测试的,编译器是能通过的,但不是我们想要的理想目标.

这种情况下就导出了logical constness,这一派主张一个const成员函数可以修改它所处理对象的某些位,但只有在客户端侦测不出来的时候才应如此.这种主张就导致了关键字mutable的诞生,它的作用是const函数实现体内可以修改由关键字mutable修饰的成员变量.比如我们修改一下前面一段代码:

点击(此处)折叠或打开

  1. class String{
  2. public:
  3.       String(char* pstring):pstring_(pstring)
  4.       {
  5.       }
  6.       char& operator[](int index)const
  7.       {
  8.             ...
  9.             return pstring_[index];
  10.       }
  11.       //add new function to get the length of string
  12.       int getLength()const
  13.       {
  14.             if(!isValidLength_)
  15.             {
  16.                   stringLength_ = strlen(pstring_);
  17.                   isValidLength_ = true;
  18.             }
  19.             return isValidLength_;
  20.       }
  21. private:
  22.       char* pstring_;
  23.       mutable bool isValidLength_;
  24.       mutable int stringLength_;
  25. };

这里新增的函数getLength虽然有const修饰,但是实现体中我们修改了关键字mutable修饰的成员变量,编译器是容许我们这么做的。

mutable修饰的类成员变量的值可以在同一个类的const成员函数中被修改.
阅读(1290) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~