单例模式
什么是单例模式
单例是设计模式里面的一种,全局有且只有一个类的static实例,在程序任何地方都能够调用到。单例模式分为懒汉模式(线程不安全)和饿汉模式;
懒汉模式
因为很懒,所以在调用方法的时候去new对象,多线程中是不安全的,需要加锁;
普通指针的懒汉模式(线程安全的,需要手动释放空间)
需要自己手动delete去释放空间;
#include <iostream>
#include <mutex>
//使用加锁的方式保证线程安全
class SingleInstance
{
public:
~SingleInstance()
{
std::cout << "析构函数" << std::endl;
}
SingleInstance(const SingleInstance&) = delete;
SingleInstance& operator =(const SingleInstance) = delete;
static SingleInstance* GetSingleInstance()
{
std::cout << "GetSingleInstance" << std::endl;
if (m_SingleInstance == nullptr)
{
//上锁
std::lock_guard<std::mutex> lk(m_mutex);
//双重判断
if (m_SingleInstance == nullptr)
{
m_SingleInstance = new SingleInstance();
}
}
return m_SingleInstance;
}
private:
explicit SingleInstance()
{
std::cout << "构造函数" << std::endl;
}
private:
static SingleInstance* m_SingleInstance;
static std::mutex m_mutex;
};
//初始化
SingleInstance* SingleInstance::m_SingleInstance = nullptr;
std::mutex SingleInstance::m_mutex;
智能指针的懒汉模式(线程安全的)
因为使用了智能指针,所以不需要手动delete去释放空间;
#include <iostream>
#include <mutex>
//使用加锁的方式保证线程安全
class SingleInstance
{
public:
~SingleInstance()
{
std::cout << "析构函数" << std::endl;
}
SingleInstance(const SingleInstance&) = delete;
SingleInstance& operator =(const SingleInstance) = delete;
static std::shared_ptr<SingleInstance> GetSingleInstance()
{
std::cout << "GetSingleInstance" << std::endl;
if (m_SingleInstance == nullptr)
{
//上锁
std::lock_guard<std::mutex> lk(m_mutex);
//双重判断
if (m_SingleInstance == nullptr)
{
m_SingleInstance = std::shared_ptr<SingleInstance>(new SingleInstance());
}
}
return m_SingleInstance;
}
private:
explicit SingleInstance()
{
std::cout << "构造函数" << std::endl;
}
private:
static std::shared_ptr<SingleInstance> m_SingleInstance;
static std::mutex m_mutex;
};
//初始化
std::shared_ptr<SingleInstance> SingleInstance::m_SingleInstance = nullptr;
std::mutex SingleInstance::m_mutex;
局部静态懒汉模式(推荐)
最推荐,最简洁,最安全的方式
这种方法又叫做 Meyers’ SingletonMeyer’s的单例, 是著名的写出《Effective C++》系列书籍的作者 Meyers 提出的。所用到的特性是在C++11标准中的Magic Static特性:
#include <iostream>
class SingleInstance
{
public:
~SingleInstance()
{
std::cout << "析构函数" << std::endl;
}
SingleInstance(const SingleInstance&) = delete;
SingleInstance& operator =(const SingleInstance) = delete;
static SingleInstance& GetSingleInstance()
{
static SingleInstance m_SingleInstance;
return m_SingleInstance;
}
private:
explicit SingleInstance()
{
std::cout << "构造函数" << std::endl;
}
private:
};
饿汉模式(手动释放)
因为已经饿的不行了,所以直接new了一个空间,线程也是安全的
#include <iostream>
class SingleInstance
{
public:
~SingleInstance()
{
std::cout << "析构函数" << std::endl;
}
SingleInstance(const SingleInstance&) = delete;
SingleInstance& operator =(const SingleInstance) = delete;
static SingleInstance* GetSingleInstance()
{
return m_SingleInstance;
}
private:
explicit SingleInstance()
{
std::cout << "构造函数" << std::endl;
}
private:
static SingleInstance *m_SingleInstance;
};
SingleInstance* SingleInstance::m_SingleInstance = new SingleInstance();
其他的版本与懒汉类似
应该在什么时候下使用单例模式?
举一个小例子,在我们的windows桌面上,我们打开了一个回收站,当我们试图再次打开一个新的回收站时,Windows系统并不会为你弹出一个新的回收站窗口。,也就是说在整个系统运行的过程中,系统只维护一个回收站的实例。这就是一个典型的单例模式运用。
继续说回收站,我们在实际使用中并不存在需要同时打开两个回收站窗口的必要性。假如我每次创建回收站时都需要消耗大量的资源,而每个回收站之间资源是共享的,那么在没有必要多次重复创建该实例的情况下,创建了多个实例,这样做就会给系统造成不必要的负担,造成资源浪费。
再举一个例子,网站的计数器,一般也是采用单例模式实现,如果你存在多个计数器,每一个用户的访问都刷新计数器的值,这样的话你的实计数的值是难以同步的。但是如果采用单例模式实现就不会存在这样的问题,而且还可以避免线程安全问题。同样多线程的线程池的设计一般也是采用单例模式,这是由于线程池需要方便对池中的线程进行控制