C++中static变量的初始化
作者:billy
版权声明:著作权归作者所有,商业转载请联系作者获得授权,⾮商业转载请注明出处
前⾔
在C语⾔中,static变量如果初始化,那么初始化发⽣在任何代码执⾏之前,属于编译期初始化。全局变量、static全局变量、static局部变量,此三者的⽣命周期、初始化⽅法完全⼀致,只是可见范围不同。
⽽在C++中我们引⼊了对象,这给全局变量的管理带领新的⿇烦。C++的对象必须由构造函数⽣成,并最终执⾏析构操作。由于构造和析构并⾮分配内存那么简单,可以说相当复杂,因此何时执⾏全局或静态对象的构造和析构呢?这需要执⾏相关代码,⽆法在编译期完成。因此不同的static变量,在不同的时间被初始化,我们需要分情况来讨论。
编译时初始化
如果静态变量本⾝是基本数据类型(POD),且初始化值是常量,那么这个初始化过程是在编译期间完成的。
static int val = 10;
static char strArray[] = "hello world !";
加载时初始化
程序被加载时⽴即进⾏的初始化。这个初始化发⽣在main函数之前。即使程序任何地⽅都没访问过该变量, 仍然会进⾏初始化,因此形象地称之为"饿汉式初始化"。
1. 静态变量是⼀个基本数据类型,但是初始值⾮常量
static int *p = new int[1024];
int x = 3;
int y = 4;
static int z = x + y;
2. 静态变量是⼀个类对象,这种情况下即使是使⽤常量初始化,也是加载时初始化,⽽不是编译时初始化
static std::string str = "Hello world !";
class MyClass {
public:
MyClass();
MyClass(int a, int b)
;};
static MyClass* MyClass1 = new MyClass();
static MyClass MyClass2;
运⾏时初始化
这个初始化发⽣在变量第⼀次被引⽤。
也就是说,从程序执⾏模型⾓度看,程序所在进程空间中,哪个线程先访问了这个变量,就是哪个线
程来初始化这个变量。因此,相对于加载初始化来说,这种初始化是把真正的初始化动作推迟到第⼀次被访问时,因⽽形象地称为"懒汉式初始化"。
int myfunc()
{
static std::string msg = "hello world !";    //运⾏时初始化
}
static 初始化的原理
例1:
int main()
{
for(int x = 5; x < 10; x++)
{
static int y = x; //第⼀次被引⽤时初始化,并且只初始化⼀次
cout << "x = " << x << ", y = " << y << endl;
}
return 0;
static修饰的变量}
输出结果:
x = 5, y = 5
x = 6, y = 5
x = 7, y = 5
x = 8, y = 5
x = 9, y = 5
例2:
int main()
{
for(int x = 5; x < 10; x++)
{
static int y = x;
cout << "x = " << x << ", y = " << y << endl;
int *p = &y;
p++;
*p = 0;
}
return 0;
}
输出结果:
x = 5, y = 5
x = 6, y = 6
x = 7, y = 7
x = 8, y = 8
x = 9, y = 9
通过两个例⼦的结果我们可以知道,静态变量的初始化就是通过静态变量后⾯的⼀个32位内存位来做记录,以标识这个静态变量是否已经初始化。每次运⾏到当前位置,会先去判断这个地址:
如果不是1,就给它赋值1,然后给变量赋值;
如果是1,直接跳过赋值代码块这样它就做到了只赋值⼀次的效果;
在例2中我们每次都将这个值赋值为0,所以程序就⼀直认为变量⼀直没有被初始化过,并每次都初始化。该操作并⾮⼀个原⼦操作,因此从代码逻辑⾓度来说,static变量并不具有“线程安全”性能。
总结
1. 如果是编译时和加载时初始化,是不会存在线程安全这个问题的。因为这两种初始化⼀定发⽣在Main函数执⾏之前,这个时候尚未进
⼊程序运⾏空间,⽽这些初始化⼀定是在单线程环境下操作的。
2. 如果是运⾏时初始化,因为⽆法保证访问这个静态变量⼀定只会从某个特定的线程中被访问,因此会存在"线程安全"的问题。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。