c++ static关键字的进阶-面试中的快问快答
摘要:static用法是面试过程中的高频问题,本文由浅入深地总结出static的一些细节。
正文
static关键字其实不难,简单的归纳成八股文:
-
不考虑类的情况
隐藏
-所有不加static的全局变量和函数具有全局可见性,可以在其他文件中使用,加了之后只能在该文件所在的编译模块中使用,即内部连接 默认初始化为0,包括未初始化的全局静态变量与局部静态变量,都存在全局未初始化区 静态变量在函数内定义,始终存在,且只进行一次初始化,具有记忆性,其作用范围与局部变量相同,函数退出后仍然存在,但不能使用。
-
考虑类的情况
static成员变量:只与类关联,不与类的对象关联。定义时要分配空间,
不能在类声明中初始化,必须在类定义体外部初始化
,初始化时不需要标示为static;可以被非static成员函数任意访问。
static成员函数:不具有this指针,无法访问类对象的非static成员变量和非static成员函数;
不能被声明为const、虚函数和volatile
;可以被非static成员函数任意访问。
面试过程中,如果你能够答出上面的知识点,那么恭喜你,基本掌握static的特点,但过于表面。面试官可能会继续追问你:
1.你说static静态局部变量只会初始化一次,那么具体是怎么实现的呢?
简单来说就是static修饰的局部静态变量附近存放着1字节的标识位(最多可以标识8个静态变量)用来标识该变量是否初始化。此外,编译器还会在标识位附近加锁用来保证局部静态变量的初始化线程安全。
2.static成员变量为什么不能在类的声明中初始化,必须要在类定义体外初始化?
静态成员变量属于类,而不属于对象,因此,静态成员变量只有一份副本,不像非静态成员变量那样每个对象都拥有自己的一份。如果在类声明中初始化,那么可能就会出现重复初始化,导致链接的时候报错,所以在类外初始化主要是为了保证只初始化一次。
3.按照上一个问题,你说静态成员变量在类定义体外显式地初始化是为了保证只初始化一次,那为什么不能像静态局部变量采用标识位来保证只初始化一次呢?
可以从生命周期上来切入,首先静态局部变量的生命周期是从第一次执行到定义静态局部变量的语句时到程序结束,所以可以设置标识位来避免重复定义。而静态成员对象的生命周期和类声明周期一样,程序开始到程序结束,在编译期间就已经为静态成员变量分配内存了,所以没办法进行判断。换句话说,静态局部变量可以理解为运行时,当然可以校验标识位,但是静态成员变量是编译时,逻辑已经固定,不存在动态来判断标识位。
4.static成员函数为什么不能被声明为const,virtual和volatile?
const:const 成员函数是用来保证对象的常量性不变,即在该函数中不会修改对象的成员变量。但是 static 成员函数并不依赖于特定的对象,它只是属于类本身,因此没有必要将其声明为 const。
virtual:virtual 是用来实现多态的关键字,它只适用于非静态成员函数。static 成员函数不依赖于任何对象,也就无法实现多态特性,因此不能被声明为virtual。 volatile:volatile 关键字用于指示编译器在优化代码时不应该对变量进行缓存或优化。但是 static 成员函数并不访问任何对象的状态,也不受任何对象状态的影响,因此没有必要使用 volatile 修饰符。
5.顺便扩展下,为什么函数在类定义体里面定义不会出现重复定义的问题?
首先,在类定义体里面定义函数,默认都是内联的,内联函数就是在调用该函数出进行插入代码段,所以不会出现重复定义的问题。但是,在内联函数的《
条款30 透彻了解inlining的里里外外
》中得出,内联与否其实是编码器自己决定的,但如果该函数比较大或者复杂,编译器可能会决定不将其作为内联函数处理,而采用普通函数的方式来处理。这时候,该函数的实现代码仍然会被放在一个单独的代码段中,但在需要调用该函数的地方,编译器会插入一条跳转指令来调用该函数。因此,这种方式仍然可以提高代码的重用性和可维护性,但不能像内联函数一样提高程序的执行效率。
至此,总结完毕,内容如有不对可以私信或留言,欢迎请批评指正,如果觉着内容对你有点帮助,可以 关注 本 公众号 点击 在看 , 转发 。