【The Cherno】关于static
static的作用分为三个领域:
- 作用在全局变量/全局函数上
- 作用在类的成员变量/成员函数上
- 作用在函数内的局部变量上
这三种作用方式作用在变量上(先不谈函数),都有相同的本质:被static修饰的变量在程序运行后存储在内存的Data Segment区域(重点不是栈段)。由此引申出这些变量的生存周期就是程序的运行周期
但是,变量的从属对象和作用域终归有些不同,所以三种表现形式还是有些不同。
全局变量/全局函数的 static
作用在全局变量或者全局函数上的static有两个作用:
- 当前编译单元会优先调用(或者说直接调用)本单元的静态全局变量
- 其它编译单元无法观测到本单元的静态全局变量
- 总的来说就是将
static全局变量标记为本单元私有的全局变量
实验文件:a.cpp,b.cpp,c.cpp
实验目的:观察两次s_Varible的输出值
1 | /* a.cpp 主文件 */ |
1 | /* b.cpp */ |
1 | /* c.cpp */ |
最后的结果:
1 | 2 |
说明:
- 第一次输出的是
a.cpp中的static int s_Varible,表明static的第一个性质,当前编译单元会优先调用(或者说直接调用)本单元的静态全局变量。 - 第二次输出的是
b.cpp中的全局int s_Varible,表明static的第二个性质,其它编译单元无法观测到a.cpp单元的静态全局变量。
类和结构体中的 static
首先说明,在C++中类和结构体除了**默认的可见性(visibility)**之外,其它完全相同。
类中的成员变量和方法经过static属性修饰后,这个成员变量也就变成了所有实例对象共有的全局变量,同时也变成了该类域下的变量。
1 | class Entity { |
这里面最最让人疑惑,也是最值得一讲的点就是:类内声明,类外定义。
正如上面的用法一样,在类中被static修饰的变量必须在类中声明,然后在类外定义。不能直接在类中赋值,或者下面的定义不写。
1 | class Entity { |
然后再来讲讲为什么还需要在类外定义。
在C++中,类一般是写在头文件中的,而头文件又可能被多个编译单元包含。前面说了,static修饰的变量在程序运行后分配在内存的Data Segment区域,具有全局的概念,如果允许类内定义,就会造成多个编译单元的重定义。因此需要把static修饰的变量的定义单独分离出来,在一个单独编译单元中定义。
但是为什么static修饰的函数允许在类内定义呢,这不会到这多个文件包含的重定义问题吗?答案是会的。但这时又有一个新的概念inline。
inline关键字修饰的函数允许:
- 允许函数调用展开为函数体,注意是允许,具体会不会展开还要看编译器心情
- 允许函数重定义
第二条性质就是关键,它虽然允许函数进行重定义,但是C++的ODR(One Definition Rule)原则规定编译器随机选择一个定义。所以这要求我们在编写inline修饰的函数时要完全一致,虽然编译器允许定义不同,但这也导致未定义行为,因为选择一个定义是随机的。
编译器允许函数在类内定义而不会出现重定义问题的答案就是:所有在类内声明定义的函数都是隐式由inline修饰的函数。
1 | class Entity { |
类的static在单例中的应用
1 | class Singleton { |
函数内的局部变量static
1 | class Singleton { |
