单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。
C++11之前,实现一个通用的泛型单例模式时,会遇到一个问题:这个泛型单例要能够创建所有的类型对象,但是这些类型的构造函数形参可能尽不相同,参数个数和参数类型可能都不相同,这导致我们不容易做一个所有类型都通用的单例。一种方法是通过定义一些创建单例的模板函数来实现。在一般情况下,类型的构造函数形参不超过6个,所以可以通过定义0~6个形参的创建单例的模板函数来实现一个通用的单例模式,代码如下所示:
#include <iostream>
using namespace std;template<typename T>
class Singleton
{
public:///支持0个参数的构造函数static T* Instance(){if (m_pInstance == nullptr){m_pInstance = new T();}return m_pInstance;}///支持1个参数的构造函数template<typename T0>static T* Instance(T0 arg0){if (m_pInstance == nullptr){m_pInstance = new T(arg0);}return m_pInstance;}///支持2个参数的构造函数template<typename T0, typename T1>static T* Instance(T0 arg0, T1 arg1){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1);}return m_pInstance;}///支持3个参数的构造函数template<typename T0, typename T1, typename T2>static T* Instance(T0 arg0, T1 arg1, T2 arg2){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2);}return m_pInstance;}///支持4个参数的构造函数template<typename T0, typename T1, typename T2, typename T3>static T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2, arg3);}return m_pInstance;}///支持5个参数的构造函数template<typename T0, typename T1, typename T2, typename T3, typename T4>static T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2, arg3, arg4);}return m_pInstance;}///支持6个参数的构造函数template<typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>static T* Instance(T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5){if (m_pInstance == nullptr){m_pInstance = new T(arg0, arg1, arg2, arg3, arg4, arg5);}return m_pInstance;}///获取单例static T* GetInstance(){if (m_pInstance == nullptr){std::logic_error("the instance is not init, please initialize the instance first");}return m_pInstance;}///释放单例static void DestoryInstance(){delete m_pInstance;m_pInstance = nullptr;}private:///不允许复制和赋值Singleton(void);virtual ~Singleton(void);Singleton(const Singleton&);Singleton& operator=(const Singleton&);private:static T* m_pInstance;
};template<class T>
T* Singleton<T>::m_pInstance = nullptr;class A
{
public:A(){cout << "construct A...." << endl;}
};class B
{
public:B(int x){m_x = x;cout << "construct B...." << endl;}
private:int m_x;
};class C
{
public:C(int x, double db){m_x = x;m_db = db;cout << "construct C...." << endl;}
private:int m_x;int m_db;
};int main()
{///创建A类型的单例Singleton<A>::Instance();///创建B类型的单例Singleton<B>::Instance(1);///创建C类型的单例Singleton<C>::Instance(1, 2.0);Singleton<A>::DestoryInstance();Singleton<B>::DestoryInstance();Singleton<C>::DestoryInstance();return 0;
}
从测试代码中可以看到,这个Singleton<T>可以创建大部分类型,支持不超过6个参数的类型。不过,从实现代码中可以看到,有很多重复的模板定义,这种定义繁琐而又重复,当参数超过6个时,我们不得不再增加模板定义。这种预先定义足够多的模板函数的方法显得重复又不够灵活。
C++11d可变参数模板正好可以消除这种重复,同时支持完美转发,即避免不必要的内存复制提高性能,又增加了灵活性。C++11实现的一个简洁通用的单例模式如下所示:
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <string.h>
using namespace std;template<typename T>
class Singleton
{
public:template<typename ...Args>static T* Instance(Args&& ...args){if (m_pInstance == nullptr) {///完美转发m_pInstance = new T(std::forward<Args>(args)...);}return m_pInstance;}///获取单例static T* GetInstance(){if (m_pInstance == nullptr){throw std::logic_error("the instance is not init, please initialize the instance first");}return m_pInstance;}static void DestoryInstance(){delete m_pInstance ;m_pInstance = nullptr;}private:Singleton(void);virtual ~Singleton();Singleton(const Singleton&);Singleton& operator=(const Singleton&);private:static T* m_pInstance;
};template<class T>
T* Singleton<T>::m_pInstance = nullptr;class A
{
public:A(const string& ){cout << "A lvalue" << endl;}A(string&& ){cout << "A rvalue" << endl;}
};class B
{
public:B(const string& ){cout << "B lvalue" << endl;}B(string&& ){cout << "B rvalue" << endl;}
};class C
{
public:C(int x, int y){cout << "C construct" << endl;}void func(){cout << "class C call func..." << endl;}
};std::once_flag init_flagA;
std::once_flag init_flagB;
std::once_flag init_flagC;void func1()
{///为了保证在多线程环境中某个函数仅被调用一次,比如,需要初始化某个对象,而这个对象只能初始化一次,就可以用std::call_once来保证在多线程环境中只被调用一次std::call_once(init_flagA, []() {Singleton<A>::Instance("123");});std::call_once(init_flagB, []() {Singleton<B>::Instance(std::move("abc"));});std::call_once(init_flagC, []() {Singleton<C>::Instance(1, 2);});
}void func2()
{///为了保证在多线程环境中某个函数仅被调用一次,比如,需要初始化某个对象,而这个对象只能初始化一次,就可以用std::call_once来保证在多线程环境中只被调用一次std::call_once(init_flagA, []() {Singleton<A>::Instance("456");});std::call_once(init_flagB, []() {Singleton<B>::Instance(std::move("efg"));});std::call_once(init_flagC, []() {Singleton<C>::Instance(3, 4);});
}int main()
{thread t1(func1);thread t2(func1);t1.join();t2.join();Singleton<C>::GetInstance()->func();getchar();Singleton<A>::DestoryInstance();Singleton<B>::DestoryInstance();Singleton<C>::DestoryInstance();return 0;
}
可以看到,C++11版本的通用单例模式的实现,没有了重复的模板定义,支持任意个数参数的类型创建,不用再担心模板函数定义得不够,还支持完美转发,无论是左值还是右值都能转发到正确的构造函数中,通过右值引用的移动语义还能进一步提高性能,简洁而优雅。