When NONCOPYABLE meets SINGLETON

When implementing a singleton class, it is usually necessary to set the access permissions of several functions related to the construction as private or protected (preferably private). But suppose there are dozens of singleton classes in a large-scale system (this is normal, singleton classes are actually one of the most commonly used designs of appearance patterns), and writing each of them in this way seems tedious. To reuse the code represented by these operations, you can use the weapon provided by the C++ language-inheritance.

Design the following base class:

1 class NonCopyable {
2 public:
3     NonCopyable() = default;
4     NonCopyable(const NonCopyable&) = delete;
5     void operator=(const NonCopyable& c) = delete;
6 };

When the singleton class inherits this class, the client cannot copy or assign the instantiated object of this singleton class, so that there is only one global object in the entire memory. The NonCopyable class implementation here refers to boost::noncopyable .

After solving the problem of constructor access permissions, there is another question, that is, how to instantiate? The classic way of implementing a singleton class is to declare a pointer to a private static class object in the class, use the get method to return a reference to this object instance, and then call other methods of the class through this reference. Very good, but it is still cumbersome to implement this for each singleton class. At this time, you can use another code reuse tool provided by C++--template. First implement a general singleton template class, and the template type parameter is passed in the name of the singleton class to be implemented, so that the construction of the singleton class (template instantiation) is completed in the compilation phase.

The general singleton template class is implemented as follows:

Copy code

 1 template <class T>
 2 class Singleton : public NonCopyable {
 3 private:
 4     static T* inst_;
 5 
 6 public:
 7     Singleton() {}
 8     virtual ~Singleton() {}
 9 
10     static T& inst()
11     {
12         if (!inst_) inst_ = new T;
13         return *inst_;
14     }
15 
16     static void uninst()
17     {
18         if (!inst_) return;
19         delete inst_;
20         inst_ = nullptr;
21     }
22 
23 };
24 //__declspec(selectany)声明使得我们可以在头文件中初始化一个全局变量
25 template <class T> __declspec(selectany)  T * Singleton<T>::inst_ = nullptr;

Copy code

Custom singleton class implementation:

Copy code

 1 class singletontest :public Singleton<singletontest>
 2 {
 3 public:
 4 singletontest(){ printf("singletontest constructor function called "); }
 5 ~singletontest(){ printf("class test object destroyed "); }
 6 //成员方法
 7 void print(){
 8 printf("singletontest::print function called ");
 9 }
10 };

Copy code

Test code:

Copy code

1 singletontest::inst().print(); //line1
2 singletontest a;  //line 2 无法阻止默认构造,理想中,单例类只允许出现类似line1的调用
3 printf("singletontest obj a addr is %d\n", &a);
4 a.print();
5 singletontest b;
6 printf("singletontest obj b addr is %d\n", &b);
7 b.print();
8 //a(b);   //singletontest继承了noncopyable,继承类初始化时先调用父类的构造函数,由于定义成私有,所以构造失败
9 //a = b;  //同上,赋值运算操作符函数同样是私有的

Copy code

 

Test Results:

After the line2 line of code is written, no error is reported during compilation, which means that the object is generated using the default structure. To prevent this behavior, the default constructor must be set as private, but the new T in the inst() method will fail, and the gain is not worth the loss.

The compiler can recognize the errors in the test line 8 and 9 code.

There is a three-tier inheritance chain (singletontest-> singleton<T>-> Noncopyable) in the code, but because there is no virtual function declared in the class, there is no workaround in the calling method, so there is no loss in performance.

Singletontest: public sington<singletontest> is written as CRTP (Curiously Recurring Template Pattern), which is often used to implement static polymorphism. This article will not introduce too much.

Finally, when discussing the creation of a singleton class in a multithreaded environment, there are often two ways of full-style and hungry-style, but modern C++ has guaranteed the thread safety of static member variable creation, so there is no discussion about this method. Necessary

Guess you like

Origin blog.csdn.net/qingzhuyuxian/article/details/108485174