定义
单例模式(Singleton Pattern,也称为单件模式),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例,并提供一个访问它的全局访问点,该实例被所有程序模块共享。
他应该满足以下几点:
1:确保一个类只有一个实例被建立
2:构造函数应该声明为非公有,从而禁止外界创建实例。
3:拷贝操作和移动操作也应该禁止。
4:提供公有方法获取实例
5:支持多线程
版本1(懒汉)
缺点:
1:没有对资源进行释放
2:不支持多线程
class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
}
return _instance;
}
private:
Singleton(){
}//构造
Singleton(const Singleton &clone){
} //拷⻉构造
Singleton& operator=(const Singleton&) {
}
stati c Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
版本2
缺点:
1:不支持多线程
class Singleton {
public:
static Singleton * GetInstance() {
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
return _instance;
}
~Singleton() {
}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton();//构造
Singleton(const Singleton &cpy); //拷⻉构造
Singleton& operator=(const Singleton&) {
}
static Singleton * _instance;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
版本3(双重校验锁)
此代码是否感觉很安全,但实际上有重大问题
首先new一个对象时,分为三个步骤,1分配内存 2构造对象 3返回指针。所以常规顺序是123,但是编译器有时候为了优化会指令重排,导致顺序变成132,这里我们考虑这么一种情况
A线程:new1分配内存并按照指令重拍之后的顺序将2地址返回_instance 指针(这时候_instance 指针是非空的,但是还没有到达构造构造,此时时间片用完,线程切换)
B线程进入,此时发现_instance 是非空的,便直接将_instance返回给调用者,而调用着使用这个没有构造的对象将会发生不可预测的后果
指令重拍原因很多,比如编译器优化或者cpu指令流水线导致,具体可以查阅相关资料
/****************************************
static Singleton * GetInstance();
加锁位置:由于除了第一次是需要new,后面调用都只是获取实例,所以在3.1处加锁会极大的损耗性能,故在3.2处通过if判断后才加锁,
两次if判空原因:主要是考虑多线程环境线程切换因素
*****************************************/
#include <mutex>
class Singleton {
// 懒汉模式 lazy load
public:
static Singleton * GetInstance() {
//std::lock_guard<std::mutex> lock(_mutex); // 3.1 切换线程
if (_instance == nullptr) {
std::lock_guard<std::mutex> lock(_mutex); // 3.2
if (_instance == nullptr) {
_instance = new Singleton();
atexit(Destructor);
}
}
return _instance;
}
private:
static void Destructor() {
if (nullptr != _instance) {
delete _instance;
_instance = nullptr;
}
}
Singleton(){
} //构造
Singleton(const Singleton &cpy){
} //拷⻉构造
Singleton& operator=(const Singleton&) {
}
static Singleton * _instance;
static std::mutex _mutex;
}
Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
版本4
解决版本三问题,但是代码显得很臃肿
#include <mutex>
#include <atomic>
class Singleton {
public:
static Singleton * GetInstance() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);//加载实例
std::atomic_thread_fence(std::memory_order_acquire);//获取内存屏障
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(_mutex);
tmp = _instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new Singleton;
std::atomic_thread_fence(std::memory_order_release);//释放内存屏障
_instance.store(tmp, std::memory_order_relaxed);
atexit(Destructor);
}
}
return tmp;
}
private:
static void Destructor() {
Singleton* tmp = _instance.load(std::memory_order_relaxed);
if (nullptr != tmp) {
delete tmp;
}
}
Singleton(){
}
Singleton(const Singleton&) {
}
Singleton& operator=(const Singleton&) {
}
static std::atomic<Singleton*> _instance;
static std::mutex _mutex;
};
std::atomic<Singleton*> Singleton::_instance;//静态成员需要初始化
std::mutex Singleton::_mutex; //互斥锁初始化
版本5
c++11 magic static 特性:如果当变量在初始化的时候,并发同时进⼊声明语句,并发线程将会阻塞等待初始化结束。
缺点:无法继承
class Singleton
{
public:
~Singleton(){
}
static Singleton& GetInstance() {
static Singleton instance;
return instance;
}
private:
Singleton(){
}
Singleton(const Singleton&) {
}
Singleton& operator=(const Singleton&) {
}
};
版本6
template<typename T>
class Singleton {
public:
static T& GetInstance() {
static T instance; // 这⾥要初始化DesignPattern,需要调⽤DesignPattern 构造函数,同时会调⽤⽗类的构造函数。
return instance;
}
protected:
virtual ~Singleton() {
}
Singleton() {
} // protected修饰构造函数,才能让别⼈继承
Singleton(const Singleton&) {
}
Singleton& operator =(const Singleton&) {
}
};
class DesignPattern : public Singleton<DesignPattern> {
friend class Singleton<DesignPattern>; // friend 能让 Singleton<T> 访问到 DesignPattern构造函数
private:
DesignPattern(){
}
DesignPattern(const DesignPattern&) {
}
DesignPattern& operator=(const DesignPattern&) {
}
}