如果单例对象构造十分耗时或者占用很多资源,但是有的对象用不到,饿汉模式也要在一开始初始化,这样就会导致启动非常慢,这种就要使用懒汉模式,懒汉模式对应饿汉模式的缺点,就像懒汉一样,什么时候用什么时候实例化,什么时候有什么时候吃。它就可以控制实例顺序。懒汉模式是使用延迟加载的思想。但是它很复杂。
class Singleton
{
public:
static Singleton* GetInstance()
{
if (_pinst == nullptr)
{
_pinst = new Singleton;
}
return _pinst;
}
void* Alloc(size_t n)
{
void* ptr = nullptr;
// ...
return ptr;
}
void Dealloc()
{
// ...
}
private:
// 构造函数私有
Singleton()
{}
Singleton(Singleton const&) = delete;
Singleton& operator=(Singleton const&) = delete;
char* _ptr = nullptr;
// 声明
static Singleton* _pinst;
};
// 定义
Singleton* Singleton::_pinst = nullptr;
懒汉模式和饿汉模式的特点差不多,无非就是懒汉模式需要的时候才创建,那这就会带来线程安全的问题,多个线程同时访问,第一次多个线程要竞争的创建类,创建好之后就不要再创建了。可以拿线程池作参考。
class Singleton
{
public:
static Singleton* GetInstance()
{
// 只有第一次为空的时候才创建,如果不为空直接返回,这样就只new了一次
// 双检查加锁
if (_pinst == nullptr) // 已经创建了就直接返回,没有就加锁创建
{
unique_lock<mutex> lock(_mutex); // RAII的加锁方式
if (_pinst == nullptr)
_pinst = new Singleton;
}
return _pinst;
// ...
// 声明
static Singleton* _pinst;
static mutex _mutex;
};
// 定义
Singleton* Singleton::_pinst = nullptr;
mutex Singleton::_mutex;
特殊的懒汉模式:
class Singleton
{
public:
static Singleton* GetInstance()
{
// 局部静态对象是在调用的时候初始化
static Singleton _s;
return &_s;
}
};
上面的这种方法在C++11之前是不能保证线程安全的,因为在这之前局部静态对象的构造函数调用初始化并不能保证线程安全,不能保证创建是否是原子的,所以在C++11后修复了这个问题,这种写法只能在支持C++11的编译器上。