const volatile mutable 的用法
_______________________________________________
相信const大家对他并不陌生,可能大家在日常的编写代码当中就会时常用到const,但是剩下的两个关键字
不知道我们有
没
有
使用
过volatile和mutable两个关键字其实不算特别常用,但是我们一定要知道这个关键字
有什么用,应该怎么用.首
现在我要说一个const操作里面比较骚的一些做法,
但是这样编不过去啊,因为MT()函数为const函数,所以不能修改i的值,但是如果我在这里使用mutable关键字的
运行结果:
这个程序有什么问题吗? 如果我们不去关心volatile关键字的话,那么这个程序你怎么看都会觉得没多大问题.但是这里
这不科学啊?? 我们再打开监视窗口看一下a的值.
举个例子我们以前写过的一个类,我们会使用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[]()co
nst当中调用但是你还说重复了一些代码
比如两次return语句,函数调用.真正该做
的是实现operator[]的机能一次并使用它两次。也就是你只需要写一个函数,令另外一个调用这个,这促使我
们将
常
量性
转移. 接下来 见证奇迹我们来看看下面这个代码是怎么实现的上述的操作的:
class TextBlock
{
public:
...
const char& operator[](std::size_t position) const
{
...
...
...
return text[position];
}
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之后就会解决这个问题,来看结果: