static变量初始化顺序
1.1 全局变量、文件域的static变量和类的static成员变量在main函数执行之前初始化
1.2 局部静态变量在第一次被使用时初始化
static变量的线程安全
2.1 非局部静态变量是线程安全的
2.2 局部静态变量在编译时,编译器的实现一般是在初始化语句之前设置一个局部静态变量的标识来判断是否已经初始化,运行的时候每次进行判断,如果需要初始化则执行初始化操作,否则不执行。这个过程本身不是线程安全的。
实例:单例模式的实现
3.1 方案一
1 | class QMManager |
此方案有线程安全问题
3.2 方案二
1 | static QMManager &instance() |
此方案在每次访问时都加锁,时间消耗比较大
3.3 方案三
1 | class QMManager |
此方案将局部静态变量变为类成员变量,这样可以保证线程安全性,但是这样的时候由于没有指定初始化顺序,导致有可能出现为初始化的问题。例子如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46//.h
class QMManager
{
protected:
static QMManager instance_;
QMManager();
~QMManager(){};
public:
static QMManager *instance()
{
return &instance_;
}
};
class QMSqlite
{
protected:
static QMSqlite instance_;
QMSqlite();
~QMSqlite(){};
public:
static QMSqlite *instance()
{
return &instance_;
}
void do_something();
};
QMManager QMManager::instance_;
QMSqlite QMSqlite::instance_;
//.cpp
QMManager::QMManager()
{
printf("QMManager constructor\n");
QMSqlite::instance()->do_something();
}
QMSqlite::QMSqlite()
{
printf("QMSqlite constructor\n");
}
void QMSqlite::do_something()
{
printf("QMSqlite do_something\n");
}
这里的执行流程:程序开始后,在执行main前,执行到QMManager,QMManager::instance;这句代码,初始化QMManager里的instance静态变量,调用到QMManager的构造函数,在构造函数里调用QMSqlite::instance(),取QMSqlite里的instance静态变量,但此时QMSqlite::instance还没初始化,问题就出现了。
3.4. 方案四
1 | //.h |
跟方案三的区别在于QMManager调用QMSqlite单例时,方案3是取到全局静态变量,此时这个变量未初始化,而方案四的单例是静态局部变量,此时调用会初始化。
跟最初方案一的区别是在main函数前就初始化了单例,不会有线程安全问题。
final
1 | template <typename T> |