1. Singleton mode
Singleton mode is the most common mode. It is often used in the development process. The purpose is:
There is only one instance. This instance can only hold one globally shared data, or only hold a pile of interfaces, etc. many singleton modes can be used.
Recently, I found some common errors / problems in the process of reviewing other people's and my own code. Here are some examples
2. Specification of singleton mode
2.1 interface type of singleton mode:
1. Return reference type
Singleton &Singleton::GetInstance() { static Singleton instance; return instance; }
2. Return pointer type
Singleton *Singleton::GetInstance() { static Singleton instance; return &instance; }
Common errors:
For c + + beginners / users who have just cut into c + + language, the concept of reference / pointer may not be clear, which may be written as:
Singleton Singleton::GetInstance() { static Singleton instance; return instance; }
In this way, the single instance effect cannot be achieved, and a copy structure will be generated every time
I'm used to using quotation. I'll take quotation as an example later
2.2 the constructor of singleton mode must be explicitly declared as non public (preferably private)
as everyone knows. If the constructor is public, it will not achieve the purpose of singleton
Common error: forget the explicit declaration (generally not stupid enough to explicitly declare the constructor and use the singleton)
class Singleton { public: static Singleton &GetInstance() { static Singleton instance; return instance; } };
Because there is no declaration, others can create it externally
2.3 in singleton mode, copy construction, copy assignment, move construction and move assignment must be disabled
I once checked the code of a colleague and wrote a single example
class Singleton { public: static Singleton &GetInstance() { static Singleton instance; return instance; } private: Singleton() = default; // The constructor is explicitly declared private }; void Func() { auto instance = Singleton::GetInstance(); }
Note:
auto does not automatically derive references
So what we do here is equivalent to:
auto &instanceRef = Singleton::GetInstance(); auto instance = instanceRef;
A copy structure is generated here
How to completely avoid this behavior from the perspective of code?
You need to explicitly prohibit all copying and moving related
The correct approach is as follows:
#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \ _CLASS_NAME(const _CLASS_NAME&) = delete; \ _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \ _CLASS_NAME(_CLASS_NAME &&) = delete; \ _CLASS_NAME& operator=(_CLASS_NAME &&) = delete; class Singleton { public: static Singleton &GetInstance() { static Singleton instance; return instance; } private: Singleton() = default; // The constructor is explicitly declared private DISABLE_COPY_AND_MOVE(Singleton); }; void Func() { auto instance = Singleton::GetInstance(); }
DISABLE_COPY_AND_MOVE explicitly deletes all four of the above
At this time, you will find
auto instance = Singleton::GetInstance();
Compile but
In this way, you can ensure that no copy or movement will occur to a single instance
Written by others
std::move / auto instance =... And other behaviors are not allowed
2.3 the destructor of a singleton class must be explicitly declared private
#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \ _CLASS_NAME(const _CLASS_NAME&) = delete; \ _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \ _CLASS_NAME(_CLASS_NAME &&) = delete; \ _CLASS_NAME& operator=(_CLASS_NAME &&) = delete; class Singleton { public: static Singleton &GetInstance() { static Singleton instance; return instance; } private: Singleton() = default; // The constructor is explicitly declared private DISABLE_COPY_AND_MOVE(Singleton); }; void Func() { auto &instance = Singleton::GetInstance(); auto ptr = &instance; delete ptr; }
If the pointer of a singleton is held in some way in the code usage scenario (especially unique_ptr), the pointer will be released when it is destructed
As described above, it is possible to delete this pointer maliciously.
After deleting the pointer, the single instance reference / pointer obtained by others will cause the program to crash
Correct practice:
#define DISABLE_COPY_AND_MOVE(_CLASS_NAME) \ _CLASS_NAME(const _CLASS_NAME&) = delete; \ _CLASS_NAME& operator=(const _CLASS_NAME &) = delete; \ _CLASS_NAME(_CLASS_NAME &&) = delete; \ _CLASS_NAME& operator=(_CLASS_NAME &&) = delete; class Singleton { public: static Singleton &GetInstance() { static Singleton instance; return instance; } private: Singleton() = default; // The constructor is explicitly declared private ~Singleton() = default; // The destructor is explicitly declared private DISABLE_COPY_AND_MOVE(Singleton); // Prohibit moving and copying };