C++ const volatile constexpr static

2021/09/09 08:04
阅读数 127

前言
这是面试官比较喜欢问的问题,咱们把它解决掉,开始。

volatile
遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。
volatile 指出 该变量是随时可能发生变化的,每次使用它的时候必须从 该变量的地址中读取。

多线程下的volatile
当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值。
volatile可以解决线程同步问题么?肯定不行啊,想啥呢?

constexpr
其实这里还是有点东西的。constexpr是C++11新引入的东西,它和const并不是一种东西哦。最早的C那会儿,const有{只读}的含义,啥意思呢?就是说,咱们只能读取这个数据而不能写入这个数据。再往后呢,C++98引入了const,不仅如此,还把新的一种语义:{常量},这是啥意思呢?各位别误会,这意思是告诉编译器,这段代码或者是常量,可以在编译阶段就将其优化掉,比如将这段代码直接计算出结果,或者直接将用到const变量的地方将其替换成直接计算结果。
C++11发现,这两种语义缠绕在const一个人身上会发生冲突,所以就将常量的概念转移给了constexpr,即被constexpr修饰的代码段或者是变量,编译器可以在编译阶段对其进行编译优化。当然了,如果某个constexpr函数并不能完成在编译阶段的优化工作也没关系,毕竟这是对编译器的一种指引,为的就是尽可能的将运行时期做的事情放在编译阶段完成。

constexpr+volatile
一个是告诉编译器可以做代码优化,一个是禁止编译器优化,很显然,这俩如果碰到一起了,编译器会听volatile的,不会再编译阶段对代码或者数据做优化。

const
这可是老生常谈的问题的。面试官最喜欢问const这个东西。咱们这回就一点点的把const全部讲解清楚!

1.基础功能-只读属性
const int i = 10;
1
这个就很好理解了,pass!

2.与#define的对比
其实看过Effective C++这本书的人都知道,尽可能用const而不要用#define,为什么呢?#define只是做字符串的字面替换而已,本身#define的常量是没有数据类型的。而const则不一样。于是编译器就无法对#define常量做类型的安全检查,但是const就可以。

3.const与函数重载
这一点想必大家都很清楚,const在函数重载上其实挺有作用的。一句话:如果参数不是指针或者是引用类型,则参数是否带有const,没关系,不算重载。

 int sum (int a) 和 int sum (const int a);
1
如果参数带指针或者是引用的话,这个const是个顶层const的话,也没关系,这不算重载。(亲,可以去了解一下什么是顶层const,什么是底层const)。
可是如果带指针或者是引用的参数,const修饰了底层,那事情就大了,这就算重载函数了!
咱们举一个特殊的例子您就知道了。类函数在屁股后面可以坠上一个const,表示对this指针的一种限制,具体的说,这个const是一种底层const,即this指针指向的内容,这块内容不能被改变。
好了,结合上面的例子咱们不难看出,这显然是一种函数的重载。

Class A {

int function ();

int function () const;

};
1
2
3
4
5
6
7
4.为什么说const节省内存空间?
咱们定义的const常量,在const创建的时候,占用了一份内存,不过呢,在被别人使用的时候,只是将这个const的内存地址丢过去了。而#define可就不一样了,每次用#define创建新的变量,总得开辟一块完整的内存。

#define PI 3.14159         //常量宏
const doulbe  Pi=3.14159;  //此时并未将Pi放入ROM中
              ......
double i=Pi;   //此时为Pi分配内存,以后不再分配!
double I=PI;  //编译期间进行宏替换,分配内存
double j=Pi;  //没有内存分配
double J=PI;  //再进行宏替换,又一次分配内存!
1
2
3
4
5
6
7
5.如何去除const约束
总之您记住,const形参既可以接受const输入,也可以接受非const输入;但是对于非const形参,就只能接受非const输入。在很久之前别人写的库函数,有很多都是非const形参的,所以很有可能出现消除const属性的场景。
做法其实非常简单,就用C++11的const_cast()即可。

static
为啥要放在这个地方介绍static呢?其实我就是觉得这几个都是关键词就放在一起了。
第一次面试兴业银行的时候,我被问到了static这个东西。当时我记得我回答的很一般(大家应该都有这种感觉吧,就是让你用的话你都oK,但是概括说一下就很僵硬)。
这里我引用知乎@Arkin的一篇文章,侵删
链接:link
 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部