const && volatile && mutable

const

const修饰普通变量或者指针

有两种写法修饰变量:

含义是类型为TYPE的变量value是不可变的,即常量。其实,对于一个非指针类型的TYPE,这两种写法都是一种含义——value值不可变。

例如:

但对于指针类型的TYPE,不同的写法含义会不同:

>指针本身是常量,不能改变:

>指针所指向的内容是常量:

>两者都是常量:

识别const修饰的是指针还是指针所指向的内容,一个较为简单的方法是沿着 * 号划一条线:

若const位于 * 的左侧,则const用来修饰的是指针指向的内容,为常量;

若const位于 * 的右侧,则const用来修饰的是指针本身,指针本身的地址为常量。

const修饰函数参数

const用来修饰函数参数,表示函数内部不能修改参数的值(确切的说是形参的值,包括参数本身以及参数中包含的值都不能在函数内部修改)。

通过这些示例我们得出结论:修饰函数参数的const通常用于该参数是指针或引用的情况,若参数的参数采用按值传递,由于函数自动产生临时变量复制参数,这样参数就不需要使用const修饰来保护。

const修饰类对象/对象指针/对象引用

const修饰类对象表示该对象为常量对象,对象中的任何成员变量都不能被修改。同理对于修饰的对象指针或者对象引用。const修饰的对象,该对象中任何非const修饰的成员函数都不能被调用,因为任何非const的成员函数都有修改成员变量的企图。

const修饰数据成员

const数据成员只在类对象的生存期内是常量,对于类而言是可重设置的。类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为在类对象未创建时,编译器不知道const数据成员的具体值。

const数据成员的初始化只能在类构造函数的初始化列表中进行。若要在类中设定恒定的常量,可以通过枚举常量实现:

枚举常量不占用对象的存储空间,编译时即被赋值。但枚举常量的局限性在于隐含的数据类型是整数,不能表示浮点数等其他类型。

const修饰成员函数

const修饰类中的成员函数,该函数将不能改变对象的成员变量。一般把const写在成员函数之后。

对于const修饰的类对象/指针/引用,只能调用类的const成员函数。

const修饰的成员函数返回值

1. 若const修饰的函数返回值为类对象,则多用于操作符重载。通常,不建议用const修饰函数返回值类型为类对象或类对象引用。原因在于,若函数返回为const对象或者是const对象引用,则返回值具有const属性,返回实例只能访问类中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作。

2. 若采用“指针传递”方式的函数返回值加const修饰,则函数返回值(指针所指向的内容)不能被修改,该返回值只能被赋值给加const修饰同类型的指针。

3. 函数的返回值采用“引用传递”。采用这种方式只出现在类的赋值函数中,目的是实现链式表达。

若赋值函数的返回值使用了const修饰,则返回值的内容不允许被修改,这样 a = b = c 依然正确,而 (a = b) = c就不正确了。

const与define宏定义

1. 编译器处理方式不同

const常量是在编译运行阶段使用
define宏是在预处理阶段展开
2. 类型安全检查不同

const常量有具体的类型,在编译阶段会进行类型检查
define宏没有类型,不做类型检查,仅仅是展开
3. 存储方式不同

const常量会在内存中分配(堆栈中)
define宏仅仅是展开,不会分配内存

volatile

本意是“易变的”,该关键字是一种类型修饰符,它修饰的变量可以被编译器未知的因素更改(如操作系统、硬件或者其他线程等)。编译器对访问volatile修饰的变量的代码不再进行优化,从而提供了对特殊地址的稳定访问。当请求使用volatile修饰的变量时,即便前面的指令刚刚读取过数据,系统也会重新从它所在的内存读取数据,并且读取后会立刻被寄存。

volatile指出 i 是随时可能发生变化的,每次使用它的时候必须重新从 i 所在的地址进行读取,因而编译器生成的汇编代码会重新从 i 的地址中读取数据并放入 b 中。而优化的做法是,由于编译器发现两次从 i 读数据的代码之间的代码片段没有对 i 进行过操作,它会自动把上次读的数据放入 b 中,而不是重新从 i 的地址读数据。这样的话,若 i 是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile关键字可以保证对特殊地址的稳定访问。

在Visual C++6.0中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过嵌入汇编代码,测试volatile关键字对程序最终代码的影响。首先用classwizard创建win32 console工程,输入代码:

在调试版本(Debug)模式下运行程序,结果如下:

i = 10

i = 32

在Release版本模式下运行程序,结果如下:

i = 10

i = 10

输出的结果表明,Release模式下,编译器对代码进行了优化。如果在声明 i 之前加上volatile关键字:

分别在调试版本和Release版本运行程序,输出都是:

i = 10

i = 32

这说明该关键字发挥作用了!

定义为volatile的变量可能会被意想不到的改变,这样编译器不会假设这个变量的值了。也就是说,优化器不对其调用进行优化,每次使用该变量时必须重新读取这个变量的值,而不是使用保存在寄存器里的备份。

下面列举使用volatile变量的情况:

并行设备的硬件寄存器,如状态寄存器
一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
多线程应用中被几个任务共享的变量
这是区分C程序员和嵌入式系统程序员最基本的问题。嵌入式系统程序员经常与硬件、中断、RTOS等打交道,这些都要求volatile变量,不懂volatile关键字将会带来灾难。

下面先来探讨几个问题:

一个参数既可以是const还可以是volatile吗?解释为什么。
一个指针可以是volatile吗?解释为什么。
下面的函数有错吗?

答案:

可以。例如只读的状态寄存器,它可以被volatile修饰,因为它可能会被意想不到的改变;它也可以被const修饰,因为程序不应该试图去修改它。
可以。但这种情况很少见。例如当一个中服务子程序修改一个指向buffer的指针时。
函数有错误。这段代码的目的是用来返回指针 ptr 所指向的值的平方。但由于ptr指向的是一个volatile类型的参数,编译器将产生类似下面的代码:

由于*ptr的值可能被意想不到的改变,因此 a 和 b 可能是不同的。这样,返回的结果可能不是所期望的平方值!正确的代码如下:

mutable

mutable意思是“可变的”,与constant(C++,const)是反义词。在C++中,为突破const限制而设置mutable关键词。mutable只能修饰类的非静态数据成员,被该关键词修饰的变量将永远处于可变的状态,即使是在const函数中。

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

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

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

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

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

本文转自:http://blog.csdn.net/wuliming_sc/article/details/3717017

悠悠论坛文章,转载请注明: 转载自悠悠博客

本文链接地址: const && volatile && mutable

Published by

紫光密码

紫光密码

程序员,爱编程;摄影师,爱纪实。 爱写散文,爱写诗。 积极向上,乐观开朗。 希望自己能给周围带来些许改变。

发表评论

电子邮件地址不会被公开。 必填项已用*标注