单例模式-看这篇就够了!!!
单列模式,顾名思义,一个类只有一个实例。所以单列模式的特征为:
只有一个实例
必须提供一个全局访问点(静态成员方法或静态静态成员变量)
不可以复制拷贝
根据以上描述的特征,那么一个简单的单例模式就诞生了,如下代码所示
template <typename T>class Singleton{public:static T* Instance(){if (m_pInstance == nullptr)m_pInstance = new T();return m_pInstance;}static void DestroyInstance(){if (m_pInstance != nullptr){delete m_pInstance;m_pInstance = nullptr;}}private:Singleton(){}~Singlento(){}Singleton(const Singleton&);Singleto& operator = (const Singleton&);private:static T* m_pInstance;};template <typename T>T* Singleton<T>::m_pInstance = nullptr;
我们分析下上面代码,如果在单线程环境下面运行,没有什么问题,假设在多线程环境中,两个线程同时运行到m_pInstance == nullptr,此时条件为真,那么就会创建两个实例,这不符合单列的特征,那么在这里需要改进,在改进前,我们先用c++11实现一个不可复制的类Noncopyable,以后让其继承该类即可
class Noncopyable{protected:Noncopyable() = default;~Noncopyable() = default;Noncopyable(const Noncopyable&) = delete;Noncopyable& operator = (const Noncopyable&) = delete;};
然后改进后的单列类如下
template <typename T>class Singleton : Noncopyable{public:static T* Instance(){std::lock_guard<std::mutex> lock(m_mutex);if (m_pInstance == nullptr){m_pInstance = new T();}return m_pInstance;}static void DestroyInstance(){std::lock_guard<std::mutex> lock(m_mutex);if (m_pInstance != nullptr){delete m_pInstance;m_pInstance = nullptr;}}private:static T* m_pInstance;static std::mutex m_mutex;};template <typename T>T* Singleton<T>::m_pInstance = nullptr;template <typename T>std::mutex Singleton<T>::m_mutex;
我们分析下上面代码,解决线程安全问题无非就是加锁,但是如果线程很多情况下,每个线程都要等拿到锁的线程运行结束后才继续执行,这样无疑会导致大量的线程阻塞,那么该如何解决呢,解决办法就是在锁之前先判断是否为nullptr,于是改进后的代码如下:
template <typename T>class Singleton : Noncopyable{public:static T* Instance(){if (m_pInstance == nullptr){std::lock_guard<std::mutex> lock(m_mutex);if (m_pInstance == nullptr){m_pInstance = new T();}}return m_pInstance;}static void DestroyInstance(){if (m_pInstance != nullptr){std::lock_guard<std::mutex> lock(m_mutex);if (m_pInstance != nullptr){delete m_pInstance;m_pInstance = nullptr;}}}private:static T* m_pInstance;static std::mutex m_mutex;};template <typename T>T* Singleton<T>::m_pInstance = nullptr;template <typename T>std::mutex Singleton<T>::m_mutex;
继续分析上面的代码,其实这就是所谓的双检锁机制。但是请注意,如果数据量很大的情况,加锁释放锁本来就是耗时的操作,所以在大数据情况下,这种双检锁机制的单列模式性能就显得堪忧了,所以我们应该避免加锁操作,于是就出现了另外一种单列模式
template <typename T>class Singleton : Noncpyable{public:static T* Instance(){return m_pInstance;}private:static T* m_pInstance;};template <typename T>T* Singleton<T>::m_pInstance = new T();
template <typename T>class Singleton : Noncopyable{public:static T* Instance(){return &m_Instance;}private:static T m_Instance;};template <typename T>T Singleton<T>::m_Instance;
这样的实现应该算是比较好了,但是还有比这更好的完美方法,利用linux的pthread_once和c++11的std::call_once
template <typename T>class Singleton : Noncopyable{public:static T* Instance(){std::call_once(m_flag,&Singleton::Init); //c++11//pthread_once(&m_ponce,&Singleton::Init); //linuxreturn m_pInstance;}static void DestroyInstance(){delete m_pInstance;m_pInstance = nullptr;}private:static T* m_pInstance;static std::once_flag m_flag;//static pthread_once_t m_ponce;static void Init(){m_pInstance = new T();}};//template <typename T>//pthread_once_t Singleton<T>::m_ponce = PTHREAD_ONCE_INIT;template <typename T>T* Singleton<T>::m_pInstance = nullptr;
另外放一个超级大招,利用c++11的可变模板参数,放一个万能的单列模式
template <typename T>class Singleton : Noncopyable{public:template <typename... Args>static T* Instance(Args... args){std::call_once(m_flag,&Singleton::Init,args);return m_pInstance;}static void DestroyInstance(){delete m_pInstance;m_pInstance = nullptr;}private:static T* m_pInstance;static std::once_flag = m_flag;template <typename... Args>static void Init(Args&&.. args){return new T(std::forward<Args>(args)...);}};template <typename T>T* Singleton<T>::m_pInstance = nullptr;template <typename T>std::once_flag Singleton<T>::m_flag;
好了,单列模式就分析到这里了,最后一种是万金油。
上一个测试代码吧
#include <iostream>#include "Singleton4.hpp"struct A{A(){std::cout << "A is constrcut!" << std::endl;num = 0;}~A(){std::cout << "A is desconstruct!" << std::endl;}int num;void add(int n){num = num + n;}int getNum(){return num;}};struct B{int a_;explicit B(int a) : a_(a){}int get(){return a_;}};int main(){A *a = Singleton<A>::Instance();A *b = Singleton<A>::Instance();std::cout << "a is" << a << std::endl;std::cout << "b is" << b << std::endl;a->add(5);std::cout << "b.num=" << b->getNum() << std::endl;b->add(10);std::cout << "a.num=" << a->getNum() << std::endl;std::cout << "b.num=" << b->getNum() << std::endl;B *bb = Singleton<B>::Instance(5);std::cout << bb << std::endl;std::cout << "bb.a=" << bb->get() << std::endl;}
由于本人水平有限,若有错误,欢迎指出,谢谢!
