介绍
单例模式是一种创建型设计模式;
常用在日志模块,数据库模块;
static
成员放在数据段(已初始化) .data
饿汉式单例模式
// 饿汉式单例模式,线程安全
// 缺点:浪费资源,程序启动资源时间变长
class Singleton
{
public:
static Singleton* getInstance() // #3获取类的唯一实例的接口
{
return &instance;
}
private:
static Singleton instance; // #2 定义一个为一类的实例对象,在.data段
Singleton(){} // #1
// 拷贝构造和拷贝赋值也要删除
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::instance;
线程安全的懒汉式单例模式
重点是锁加上双重判断线程安全
写法一:使用互斥锁实现
#include <mutex>
std::mutex mtx;
class Singleton
{
public:
static Singleton* getInstance() // #3获取类的唯一实例的接口
{
// 锁放最外面太重了,导致已经创建了单例之后,获取单例还得加锁
// 锁+双重判断 确保线程安全
if (nullptr == instance)
{
lock_guard<std::mutex> guard(mtx);
if (nullptr == instance)
{
instance = new Singleton(); // 第一次使用时创建单例
}
}
return instance;
}
private:
static Singleton *volatile instance; // #2 定义一个为一类的实例对象,在.data段
Singleton() {} // #1
// 拷贝构造和拷贝赋值也要删除
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton * volatile Singleton::instance = nullptr;
相关说明
instance = new Singleton()做以下三件事
开辟内存
给instance赋值
构造对象
CPU可能进行 reorder 操作导致后两个操作颠倒,获得一个没有构造的instance,所以要保证该语句的原子性
写法二:使用C++11静态局部变量实现
class Singleton
{
public:
static Singleton* getInstance() // #3获取类的唯一实例的接口
{
// 静态局部变量初始化,在汇编指令上已经自动添加线程互斥指令了
static Singleton instance;
return &instance;
}
private:
Singleton() {} // #1
// 拷贝构造和拷贝赋值也要删除
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};