C++ — const volatile mutable的用法

来源:互联网 发布:阿拉曼战役 知乎 编辑:程序博客网 时间:2024/06/15 20:02

const volatile mutable的用法_______________________________________________


相信const大家对他并不陌生,可能大家在日常的编写代码当中就会时常用到const,但是剩下的两个关键字不知道我们有

使用过volatile和mutable两个关键字其实不算特别常用,但是我们一定要知道这个关键字有什么用,应该怎么用.首

先const的基本操作我曾经写过一篇博客:const的基本使用

现在我要说一个const操作里面比较骚的一些做法,

举个例子我们以前写过的一个类,我们会使用operator[]来返回一个reference的指向,这个一般情况我们都会写一个

const的也会写一个非const的opeartor[].这是我们最常见的一个代码:

T& operator[](int position)   {      return xxx[position];  }  const T& operator[](int position) const  {      return xxx[position];  }  

这是我们平时写的初级的代码,但是现在当我们要写一个TextBlock内的opeartor[]不单只返回一个referencr了

,也可能执行边界检查,日志访问信息,还有什么数据完善性检验等等一大堆繁琐的代码,这个时候当你实现

operator[] const和operator[]() const,的时候两份代码大部分都一样,这里伴随的是代码重复,编译时间变长,

维护代码膨胀等等头疼的问题. 当然啦,你可以让上述那些繁琐的函数全部封装的别的函数中,然后分别在

operator[]()和operator[]()const当中调用但是你还说重复了一些代码比如两次return语句,函数调用.真正该做

的是实现operator[]的机能一次并使用它两次。也就是你只需要写一个函数,令另外一个调用这个,这促使我们将

量性转移. 接下来 见证奇迹我们来看看下面这个代码是怎么实现的上述的操作的:

class TextBlock  {  public:      ...      const char& operator[](std::size_t position) const      {          ...          ...          ...          return text[position];      }        const char& operator[](std::size_t position)      {          return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);      }  };  

来仔细看这个操作;return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);

首先把*this强制转换为const TextBlock,再然后调用const的operator[],最后再把const的operator[]的返回值的

const常量性取消,然后返回一个非const的值. 这里的调用实在是太妙了,我们可以思考一下,好好想想这里的深意.

但是会有人说,为什么不用const operator[]调用operator[]呢,这样强制两个都可以行的通啊.这样想是错的!

令const版本调用调用no-const版本以避免重复并不是你该做的事情. 记住const所修饰函数的承诺就是我绝对不会修

改你,no-const函数可没有这种承诺,所以你让一个const函数去调用一个no-const函数是不现实的. over

其实const有很多可以玩的属性,只要我们想到就可以去实现,这里就说这么一个就ok. 接下来我们来瞧瞧另外两个

关键字.



mutable

———————————————————————————————————————


其实呢,这个关键字的作用就是mutalbe的中文意思是“可变的,易变的”,跟constant是反义词就是我们上边说的

const在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量(mutable只能由于修饰类的

非静态数据成员),将永远处于可变的状态,即使在一个const函数中。


实际运用起来也非常容易,就是你想改变的元素被const修饰了,你就往它前面加上mutable那么你就无敌了..

我就举一个最简单的例子,我定一个AA类,我在AA类中定义一个MT()函数,该函数属性为const属性,再然后我

想在MT()函数中添加该函数执行多少次时,程序编不过去了. 因为const修饰的函数里面的所有值都不能修改.


代码举例:

class AA{public:void MT() const{i++;cout << "hehe";cout << "执行了" << i << "次该程序";}private:int i = 0;};

但是这样编不过去啊,因为MT()函数为const函数,所以不能修改i的值,但是如果我在这里使用mutable关键字的

话,现在我们把i加上mutable关键字,这样它永远就是一个可变的了. 来我们加上去试一试,

class AA{public:void MT() const{i++;cout << "hehe" << "   ";cout << "执行了" << i << "次该程序" << endl;;}private:mutable int i = 0;};int main(){AA a;a.MT();a.MT();a.MT();a.MT();return 0;}

运行结果:



这就是mutable的最简单的一个应用,以后可以根据需求来使用~ 


volatile

______________________________________________________________________


一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地

说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下

面是volatile变量的几个例子:

1.并行设备的硬件寄存器(如:状态寄存器

2.一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3.多线程应用中被几个任务共享的变量


看下面例题:

int square(volatile int *ptr){    return *ptr * *ptr;}

这个程序有什么问题吗? 如果我们不去关心volatile关键字的话,那么这个程序你怎么看都会觉得没多大问题.但是这里

面问题大这ne, 首先参数声明为volatile就是表明*ptr可能会随时改变.上述代码运行时,编译器可能产生这样的代码:

int square(volatile int *ptr){    int a,b;    a = *ptr;    b = *ptr;    return a * b;}


因为你的*ptr是随时都可以意想不到的变化,所以有可能a*b的时候,a b的值不相同. 这样你就得到一个错误的结果

改正后的程序:

int square(volatile int *ptr){    int a;    a = *ptr;    return a * a;}


第二个问题,看如下代码:


#include<iostream>  #include<Windows.h>  #include<assert.h>    using namespace std;    int main()  {      const int a = 2;      int *p = const_cast<int*>(&a);      *p = 3;      cout << a << endl;      system("pause");      return 0;  }  


我们有理由的认为在内存当中a的值被修改为3,但是结果呢? 我们来看一看




这不科学啊?? 我们再打开监视窗口看一下a的值.



我们都知道监视窗口看到的都是从内存当中拿到的,但是为什么内存当中为3,打印出来就是2呢? 我来解释一下.

C++编译器具有优化功能,当你定一个const的常量的时候,系统觉得它不会被改变了,于是做一个优化把该常量存到寄

存器当中,下次访问的过程更快速一点. 所以当显示窗口读取数据的时候,他会直接去寄存器当中读取数据.而不是去

内存,所以导致我们明明该掉了a的值,却打印不出来.

这个时候该我们的volatile出马了,往i前面加一个volatile之后就会解决这个问题,来看结果:








原创粉丝点击