The singleton pattern can ensure that there is only one instance of a class and operate the class through the global access point. In actual coding, if you want to develop a generic singleton pattern and the singleton can create all type objects, you will inevitably encounter the problem of different types or numbers of constructor parameters, resulting in the need to implement many constructors in the singleton, Most of these works are repetitive, which brings a lot of repetitive workload to coding.
The new features provided by C++11 can help us solve these problems. In particular, if we implement a general generic single instance class, we can use the variable parameter template in C++11 to eliminate this duplication. At the same time, we can use perfect forwarding to avoid unnecessary memory replication, so as to improve the performance of the program and increase the flexibility of the code. The following can show the convenience and flexibility of the new features of C++11 through the comparison between the two pieces of code.
1. Implement a generic singleton before C + + 11
Generally, there are no more than 6 constructor parameters. If you want to implement a general single instance template class, you can write it as follows. The code is as follows:
template <class T> class SingleClass{ public: //Constructor that implements 0 parameters static T* instance(){ if(nullptr == m_pInstance){ m_pInstance = new T(); } return m_pInstance; } //Implement a constructor with 1 parameter template <typename T0> static T* instance(T0 arg1){ if(nullptr == m_pInstance){ m_pInstance = new T(arg1); } return m_pInstance; } //Functions with 2,3,4,5 parameters in the middle are not implemented in turn. Here, 0,1,6 parameters are implemented //Implement a constructor with 1 parameter 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(nullptr == m_pInstance){ m_pInstance = new T(arg0,arg1,arg2,arg3,arg4,arg5); } return m_pInstance; } //Get singleton static T* GetInstance(){ if(nullptr == m_pInstance) throw std::logic_error("Pointer not initialized,You need to initialize with a constructor first"); return m_pInstance; } //Release single case static void DestoryInstance(){ if(m_pInstance){ delete m_pInstance; m_pInstance = nullptr; } } private: SingleClass(){}; virtual ~SingleClass(){}; SingleClass(const SingleClass &){}; SingleClass &operator = (const SingleClass&){}; static T *m_pInstance; };
In the above code, in the constructor that implements 0-6 formal parameters, there are many repeated code writing in order to achieve common (but only support 0-6 formal parameters). This kind of work is very cumbersome and inflexible for coders. The following code is implemented with the new features of C++11. You can make a comparison.
2 generic singleton of new features of C + + 11
template <class T> class SingleClass{ public: template <typename ...Args> static T* instance(Args&&... args){ if(nullptr == m_pInstance){ m_pInstance = new T(std::forward<Args>(args)...); } return m_pInstance; } //Get singleton static T* GetInstance(){ if(nullptr == m_pInstance) throw std::logic_error("Pointer not initialized,You need to initialize with a constructor first"); return m_pInstance; } //Release single case static void DestoryInstance(){ if(m_pInstance){ delete m_pInstance; m_pInstance = nullptr; } } private: SingleClass(){}; virtual ~SingleClass(){}; SingleClass(const SingleClass &){}; SingleClass &operator = (const SingleClass&){}; static T *m_pInstance; };
As shown in the above code, three new features of C++11 are used in the code, namely:
- Template variable parameters
- rvalue reference
- Perfect forwarding
The use of new features eliminates the definition of duplicate templates, and there is no limit on the number of formal parameters from 0 to 6. Templates with arbitrary parameters can be realized; Perfect forwarding can forward the original definition of parameters to the constructor, and the right value reference can also reduce memory replication and improve code performance.
Of course, in the above singleton construction, you only need to optimize. I wonder if you have noticed that in the part defined by private, the default constructor, destructor, copy constructor and copy assignment function of singleton class are defined as private, and the compiler is prohibited from providing these functions, In addition to being written in private, the compiler can also be prevented from generating by default by means of = delete. For the description of = delete, please refer to the following article:
Use of = defaule and = delete in C + +
In this article, the above code can be modified to:
template <class T> class SingleClass{ public: SingleClass() =delete; virtual ~SingleClass() =delete; SingleClass &operator = (const SingleClass&) =delete; SingleClass(const SingleClass &) =delete; template <typename ...Args> static T* instance(Args&&... args){ if(nullptr == m_pInstance){ m_pInstance = new T(std::forward<Args>(args)...); } return m_pInstance; } //Get singleton static T* GetInstance(){ if(nullptr == m_pInstance) throw std::logic_error("Pointer not initialized,You need to initialize with a constructor first"); return m_pInstance; } //Release single case static void DestoryInstance(){ if(m_pInstance){ delete m_pInstance; m_pInstance = nullptr; } } private: static T *m_pInstance; };