C++11单例模式
# Meyer's Singleton 局部静态变量
参考: https://blog.csdn.net/lgfun/article/details/105810039 (opens new window)
函数内部的静态局部变量的初始化, 是在函数第一次调用时执行; 在之后的调用中不会对其初始化。
C++11规定, 静态局部变量在多线程情况下初始化,由编译器保证其是线程安全的。因此可以借用此特性得到一种目前最安全便捷的单例模式,即在函数内部定义static局部变量作为单例。这就是Effective C++ 中提出的 Meyers' Singleton
这样,只有当第一次访问getInstance()方法时才创建实例。C++0x之后该实现是线程安全的,C++0x之前仍需加锁。
class Singleton
{
private:
Singleton() { };
~Singleton() { };
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
public:
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
};
2
3
4
5
6
7
8
9
10
11
12
13
14
验证:
#include <iostream>
#include <thread>
class Singleton {
private:
Singleton() {
std::cout << "构造函数" << std::endl;
};
~Singleton() {
std::cout << "析构函数" << std::endl;
};
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton& getInstance() {
//返回local static的引用
static Singleton instance;
return instance;
}
};
int main(void)
{
for (int i = 0; i < 10; ++i) {
std::thread([]() {
std::cout << &Singleton::getInstance() << std::endl;
}).join();
}
std::cout << &Singleton::getInstance() << std::endl;
return 0;
}
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
# 补充:C++中static对象的初始化
# non-local static对象(函数外)
C++规定,non-local static 对象的初始化发生在main函数执行之前,也即main函数之前的单线程启动阶段,所以不存在线程安全问题。但C++没有规定多个non-local static 对象的初始化顺序,尤其是来自多个编译单元的non-local static对象,他们的初始化顺序是随机的。
# local static 对象(函数内)
对于local static 对象,其初始化发生在控制流第一次执行到该对象的初始化语句时。多个线程的控制流可能同时到达其初始化语句。
在C++11之前,在多线程环境下local static对象的初始化并不是线程安全的。具体表现就是:如果一个线程正在执行local static对象的初始化语句但还没有完成初始化,此时若其它线程也执行到该语句,那么这个线程会认为自己是第一次执行该语句并进入该local static对象的构造函数中。这会造成这个local static对象的重复构造,进而产生内存泄露问题。所以,local static对象在多线程环境下的重复构造问题是需要解决的。
而C++11则在语言规范中解决了这个问题。C++11规定,在一个线程开始local static 对象的初始化后到完成初始化前,其他线程执行到这个local static对象的初始化语句就会等待,直到该local static 对象初始化完成。