const、volatile、mutable的用法

const修饰普通变量和指针

const修饰变量,一般有两种写法:

const TYPE value;
TYPE const value;

这两种写法在本质上是一样的。它的含义是:const修饰的类型为TYPE的变量value是不可变的。对于一个非指针的类型TYPE,无论怎么写,都是一个含义,即value值不可变。 例如:

const int nValue;    //nValue是const
int const nValue;    //nValue是const

但是对于指针类型的TYPE,不同的写法会有不同情况:
指针本身是常量不可变

(char*) const pContent;

指针所指向的内容是常量不可变

const (char) *pContent;
(char) const *pContent;

两者都不可变

const char* const pContent;

识别const到底是修饰指针还是指针所指的对象,还有一个较为简便的方法,也就是沿着*号划一条线:
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。

const修饰函数参数

const修饰函数参数是它最广泛的一种用途,它表示在函数体中不能修改参数的值(包括参数本身的值或者参数其中包含的值):

void function(const int Var);     //传递过来的参数在函数内不可以改变(无意义,该函数以传值的方式调用)
void function(const char* Var);   //参数指针所指内容为常量不可变
void function(char* const Var);   //参数指针本身为常量不可变(也无意义,var本身也是通过传值的形式赋值的)
void function(const Class& Var); //引用参数在函数内不可以改变

参数const通常用于参数为指针或引用的情况,若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const修饰。

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

const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。
const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
例如:

class AAA
{void func1();
void func2() const;
}
const AAA aObj;
aObj.func1(); 错误
aObj.func2(); 正确const AAA* aObj = new AAA();
aObj->func1(); 错误
aObj->func2(); 正确

const修饰数据成员

const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么,例如:

class A
{const int size = 100; //错误int array[size];       //错误,未知的size
}

const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常量,可以用类中的枚举常量来实现,例如:

class A
{
…enum {size1=100, size2 = 200 };int array1[size1];int array2[size2];
…
}

枚举常量不会占用对象的存储空间,他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。
const修饰成员函数

const修饰类的成员函数,用const修饰的成员函数不能改变对象的成员变量。一般把const写在成员函数的最后:

class A
{…
void function()const; //常成员函数, 它不改变对象的成员变量. 也不能调用类中任何非const成员函数。
}

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

const修饰成员函数的返回值

1、一般情况下,函数的返回值为某个对象时,如果将其声明为const时,多用于操作符的重载。通常,不建议用const修饰函数的返回值类型为某个对象或对某个对象引用的情况。原因如下:如果返回const对象,或返回const对象的引用,则返回值具有const属性,返回实例只能访问类A中的公有(保护)数据成员和const成员函数,并且不允许对其进行赋值操作,这在一般情况下很少用到。

2、如果给采用“指针传递”方式的函数返回值加const修饰,那么函数返回值(即指针所指的内容)不能被修改,该返回值只能被赋给加const 修饰的同类型指针:

const char * GetString(void);

如下语句将出现编译错误:

char *str=GetString();

正确的用法是:

const char *str=GetString();

3、函数返回值采用“引用传递”的场合不多,这种方式一般只出现在类的赙值函数中,目的是为了实现链式表达。如:

class A
{
…A &operate= (const A &other); //赋值函数
}
A a,b,c; //a,b,c为A的对象
…
a=b=c;   //正常
(a=B)=c; //不正常,但是合法

若赋值函数的返回值加const修饰,那么该返回值的内容不允许修改,上例中a=b=c依然正确。(a=b)=c就不正确了。

const常量与define宏定义的区别

编译器处理方式不同

define宏是在预处理阶段展开。
const常量是编译运行阶段使用。

类型和安全检查不同

define宏没有类型,不做任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。

存储方式不同

define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
const常量会在内存中分配(可以是堆中也可以是栈中)。

volatile关键字

volatile的本意是“易变的”,volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被寄存。例如:

volatile int i=10;
int a = i;
。。。//其他代码,并未明确告诉编译器,对i进行过操作
int b = i;

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

注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无volatile关键字,对程序最终代码的影响。首先用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码:

#include <stdio.h>
void main()
{
int i=10;
int a = i;printf("i= %d/n",a);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm {mov dword ptr [ebp-4], 20h
}int b = i;
printf("i= %d/n",b);
}

然后,在调试版本模式运行程序,输出结果如下:
i = 10
i = 32
然后,在release版本模式运行程序,输出结果如下:
i = 10
i = 10

输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。下面,我们把 i的声明加上volatile关键字,看看有什么变化:

#include <stdio.h>
void main()
{
volatile int i=10;
int a = i;printf("i= %d/n",a);
__asm {mov dword ptr [ebp-4], 20h
}int b = i;
printf("i= %d/n",b);
}

分别在调试版本和release版本运行程序,输出都是:
i = 10
i = 32
这说明这个关键字发挥了它的作用!

关于volatile的补充信息:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求volatile变量。不懂得volatile内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑这否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile的重要性:
1). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数有什么错误:

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

下面是答案:
1). 是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3). 这段代码的有个恶作剧。这段代码的目的是用来返指针ptr指向值的平方,但是,由于ptr指向一个volatile型参数,编译器将产生类似下面的代码:

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

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

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

mutable关键字

mutalbe的中文意思是“可变的,易变的”,跟constant(既C++中的const)是反义词。在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量(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修饰的函数里面也能被修改。

[转] C++中 const, volatile, mutable用法相关推荐

  1. C/C++中const关键字的用法及其与宏常量的比较

    1.const关键字的性质 简单来说:const关键字修饰的变量具有常属性. 即它所修饰的变量不能被修改. 2.修饰局部变量 1 const int a = 10; 2 int const b = 2 ...

  2. 转:C++中const、volatile、mutable的用法

    const修饰普通变量和指针 const修饰变量,一般有两种写法: const TYPE value; TYPE const value; 这两种写法在本质上是一样的.它的含义是:const修饰的类型 ...

  3. C++中const、volatile、mutable的用法

    From: http://blog.csdn.net/wuliming_sc/article/details/3717017 const修饰普通变量和指针 const修饰变量,一般有两种写法: con ...

  4. [转]C++中const、volatile、mutable的用法

    原文:https://blog.csdn.net/imJaron/article/details/79657642 const意思是"这个函数不修改对象内部状态". 为了保证这一点 ...

  5. C++中关键字volatile和mutable用法

    C/C++中的volatile关键字和const对应,用来修饰变量,用于告诉编译器该变量值是不稳定的,可能被更改.使用volatile注意事项: (1). 编译器会对带有volatile关键字的变量禁 ...

  6. C++ 中const的用法,特别是用在函数前面与后面的区别!

    原文链接:https://www.cnblogs.com/doker/p/11051175.html 目录 第一:const修饰函数的参数 第二:用const修饰函数的返回值 第三:const 成员函 ...

  7. C++中const关键字用法详解及实例和源码下载(一)

    最近在学习C++基础部分,看了两天书,已经看过了一遍,半知半解,回过头来重新看第二遍,深入了解一下C++的基础知识.现在读到了const关键字的用法,书上面讲解的时候并没有给出完整的实例,只是理论的讲 ...

  8. C++学习笔记:const和mutable的多种用法【Cherno】

    先来带着几个问题来: 为什么要const.mutable声明,const.mutable的运用场景是什么: 1:为什么要有const.mutable声明: const声明相当于你向系统的一个承诺,承诺 ...

  9. C++中const指针用法汇总

    这里以int类型为例,进行说明,在C++中const是类型修饰符: int a; 定义一个普通的int类型变量a,可对此变量的值进行修改. const int a = 3;与 int const a ...

最新文章

  1. 通配符?子字符串匹配主字符串次数_突破LeetCode,拿BAT大厂offer之《正则表达式匹配》(动态规划)...
  2. 计算机影视后期处理论文,影视后期技术发展前景论文
  3. 搭建prometheus+grafana监控系统
  4. python中打印二维列表数据
  5. 殊途同归的fork()
  6. undo表空间暴长,如何取消自动扩展
  7. python logging模块使用总结
  8. XidianOJ 1090 爬树的V8
  9. Ceres Solver: 高效的非线性优化库(二)实战篇
  10. 如何使用python进行批处理
  11. 倒计时 4 天!年度开发者盛会 Unite Shanghai 2019 全日程揭晓(附表)
  12. 多租户数据中心采用SDN的优势和挑战
  13. GDI和GUI的区别
  14. 5 年迭代 5 次,抖音基于 Flink 的推荐系统演进历程
  15. 程序员面试宝典pdf
  16. 如何使用krpano全景资源下载器来下载720yun的全景图
  17. 计算机中单位换算,计算机常用单位换算
  18. uni-app自动定位当前位置
  19. ps3无限服务器连接,[攻略] (转) [分享] PS3常见错误代码及解决办法
  20. ubuntu设置相机为固定焦距

热门文章

  1. centos7.3二进制安装mariadb10.2.8
  2. 设置对话框的小三角方法
  3. VC编译项目时缺少atlrx.h的解决办法
  4. 通过命令行创建MAVEN多模块项目
  5. 考虑用静态工厂方法代替构造器
  6. java笔记:第6章 面向对象程序设计
  7. 北京地铁规划大全(图),买房子可以参考一下
  8. 在.net平台上运行伪JAVA
  9. java中怎么判断字符相等_Java中如何判断两个字符串相等
  10. Java web(2012/2/23)