C++ singleton pattern


The singleton pattern, also known as the singleton pattern and the monadic pattern, is probably the most widely used design pattern. The intent is to guarantee that a class has exactly one instance, and to provide a global access point to it, that instance is shared by all program modules. There are many places that need such functional modules, such as system log output, GUI applications must be a single mouse, MODEM connection requires one and only one telephone line, the operating system can only have one window manager, and a PC is connected to a keyboard. .
There are many ways to implement the singleton pattern. In C++, you can even do this directly with a global variable, but such code is very inelegant. Using a global object guarantees easy access to instances, but does not guarantee that only one object is declared—that is, in addition to a global instance, local instances of the same class can still be created.
The book "Design Patterns" gives a very good implementation, define a singleton class, use the class's private static pointer variable to point to the only instance of the class, and use a public static method to get the instance.
The singleton pattern provides a solution to the problem by managing its only instance through the class itself. The only instance is an ordinary object of the class, but the class is designed so that it can only create one instance and provide global access to this instance. The only instance class Singleton hides the operation of creating an instance in a static member function. It is customary to call this member function Instance(), and its return value is a pointer to a unique instance.


class CSingleton
{
private:
CSingleton() //Constructor is private
{
}
static CSingleton *m_pInstance;
public:
static CSingleton * GetInstance()
{
if(m_pInstance == NULL) //Determine whether to call
m_pInstance = new CSingleton() for the first time;
return m_pInstance;
}
};
The only way for users to access a unique instance is the GetInstance() member function. Without passing this function, any attempt to create an instance will fail because the class's constructor is private. GetInstance() uses lazy initialization, which means its return value is created when the function is first accessed. This is a bulletproof design - all subsequent calls to GetInstance() return a pointer to the same instance:
CSingleton* p1 = CSingleton :: GetInstance();
CSingleton* p2 = p1->GetInstance();
CSingleton & ref = * CSingleton :: GetInstance();
With a slight modification to GetInstance, this design template can be applied to variable multi-instance situations, such as a class that allows up to five instances.

The singleton class CSingleton has the following characteristics:
it has a static pointer m_pInstance to a unique instance, and it is private;
it has a public function that can get the unique instance and create it when needed;
its construction Functions are private so that instances of the class cannot be created from elsewhere.
Most of the time, such an implementation will not present a problem. Experienced readers may ask, when will the space pointed to by m_pInstance be released? The more serious question is, when does the instance's destructor execute?
If there are necessary operations in the destructing behavior of the class, such as closing files and releasing external resources, then the above code cannot achieve this requirement. We need a way to gracefully delete the instance.
You can call GetInstance() at the end of the program and use the delete operation on the returned pointer. Doing this does the trick, but it's not only ugly, it's error-prone. Because such additional code is easy to forget, and it is difficult to ensure that after delete, no code will call the GetInstance function again.
A proper way is to let the class know to delete itself at the right time, or hang the operation of deleting itself at a suitable point in the operating system, so that it will be automatically executed at the right time.
We know that when the program ends, the system will automatically destruct all global variables. In fact, the system will also destruct all static member variables of the class, just as these static members are also global variables. Using this feature, we can define such a static member variable in a singleton class, and its only job is to delete the instance of the singleton class in the destructor. Such as the CGarbo class in the following code (Garbo means garbage worker):
class CSingleton
{
private:
CSingleton()
{
}
static CSingleton *m_pInstance;
class CGarbo //Its only job is to delete the instance of CSingleton in the destructor
{
public:
~CGarbo()
{
if(CSingleton::m_pInstance)
delete CSingleton::m_pInstance;
}
};
static CGarbo Garbo; //Define a static member variable, when the program ends, the system will automatically call its destructor
public:
static CSingleton * GetInstance()
{
if(m_pInstance == NULL) //Determine whether to call m_pInstance for the first time
= new CSingleton();
return m_pInstance;
}
};
CSingleton * CSingleton::m_pinstnce=NULL;
CSingleton:: CGarbo CSingleton::Garbo =CSingleton::CGarbo();//Initialization, otherwise it will not be investigated destructor The

class CGarbo is defined as a private nested class of CSingleton, to prevent the class from being abused elsewhere.
When the program ends, the system calls the destructor of the static member Garbo of CSingleton, which deletes the only instance of the singleton.
Using this method to release a singleton object has the following characteristics:
define a dedicated nested class inside the singleton class;
Define private static members dedicated to release in the singleton class;
use the feature of destructing global variables at the end of the program to select the final release time;
code using singleton does not need any operations, and does not need to care about the release of objects.

Further discussion
But adding a static object of a class is always unsatisfactory, so some people use the following method to reimplement the singleton and solve its corresponding problems, the code is as follows:

class CSingleton
{
private:
CSingleton() //construction The function is private
{
}
public:
static CSingleton & GetInstance()
{
static CSingleton instance; //local static variables
return instance;
}
};

using local static variables, a very powerful method, fully implements the characteristics of a singleton, and the code The amount is less, and there is no need to worry about the problem of singleton destruction.
But using this method will also cause problems. When the following method uses a singleton, the problem comes,
Singleton singleton = Singleton :: GetInstance();
In this way, there is a problem of class copying, which violates the characteristics of singleton . The reason for this problem is that the compiler will generate a default constructor for the class to support copying of the class.
In the end, there is no way. We have to prohibit class copying and class assignment, and prohibit programmers from using singletons in this way. At that time, the leader meant that the GetInstance() function returns a pointer instead of returning a reference. The code of the function is changed to the following:


class CSingleton
{
private:
CSingleton() //The constructor is private
{
}
public:
static CSingleton * GetInstance()
{
static CSingleton instance; //local static variable
return &instance;
}
};
But I always feel bad, why Don't let the compiler do that. At this time, I remembered that the constructor for declaring class copy and overloading the = operator can be displayed. The new singleton class is as follows:
class CSingleton
{
private:
CSingleton() //The constructor is private
{
}
CSingleton(const CSingleton &);
CSingleton & operator = (const CSingleton &);
public:
static CSingleton & GetInstance()
{
static CSingleton instance; //local static variable
return instance;
}
};

Regarding Singleton(const Singleton); and Singleton & operate = (const Singleton&); functions, need to be declared private, and only declare Not implemented. In this way, if the singleton is used in the above way, whether it is in a friend class or other, the compiler will report an error.
I don't know if there will be any problems with such a singleton class, but it is basically no problem to use it like this in the program.
class Lock
{
private:
CCriticalSection m_cs;
public:
Lock(CCriticalSection cs) : m_cs(cs)
{
m_cs.Lock();
}
~Lock()
{
m_cs.Unlock();
}
};

class Singleton
{
private:
Singleton() ;
Singleton(const Singleton &);
Singleton& operator = (const Singleton &);

public:
static Singleton *Instantialize();
static Singleton *pInstance;
static CCriticalSection cs;
};

Singleton* Singleton::pInstance = 0;

Singleton* Singleton:: Instantialize()
{
if(pInstance == NULL)
{ //double check
Lock lock(cs); //Use lock to achieve thread safety, use resource management classes to achieve exception safety
//Use resource management classes, when exceptions are thrown When the resource management class object will be destructed, destruction always occurs whether it is because of an exception thrown or the end of a statement block.
if(pInstance == NULL)
{
pInstance = new Singleton();
}
}
return pInstance;
}

The reason why pInstance is empty is judged twice in the Instantialize function, because the object is generated once the method is called, and pInstance == NULL is false in most cases. If you follow the original method, every time you get an instance, Need to lock, the efficiency is too low. The improved method only needs to be locked when it is called for the first time, which can greatly improve the efficiency.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324450528&siteId=291194637