Singleton mode C ++ implementation research (reposted)

zhaozj2021-02-08  324

SINGLETON mode of C implementation research

Zhang Youbang

Summary This article proposes three Singleton mode implementations, and has been comparative analysis.

Keyword design pattern, Singleton

Singleton mode is a very common design pattern. "Design Patterns" defines it as: EnSure a class Only HAS One Instance, And Provide A Global Point of Access To It. That is to say, only one instance can only be existing in the life cycle of the entire application. The user accesses the instance through a global access point. This is the two most basic features of Singleton, but also first considers when implementing. Singleton's application is wide, it can be typically used to indicate system components that have unique features, such as database access components, and the like. This is a detailed instructions on "Design Patterns", which is not detailed here.

There are many ways to achieve Singleton, but they are inseparable from two most basic principles. First, to make Singleton only a globally unique instance, our usual practice is to privatize its constructor and copy constructor. Furthermore, Singleton's global unique instance is usually a Static variable, which uses the inherent advantages of the language. The few implementations given in this paper are relatively simple and easy to understand. In general, they are sufficient to meet the requirements. But the disadvantage is also inevitable, and the following analysis is analyzed.

First, based on the implementation of template functions

First see the realization code:

Class mysingleton1

{

Private:

Mysingleton1 () {cout << _t ("construct mysingleton1") << endl;}

MYSINGLETON1 (const mySingleton1 &) {} // copy constructor

Mysingleton1 & operator = (const mysingleton1 &) {} // assignment function

Template

Friend T & GetInstanceRef ();

PUBLIC:

~ Mysingleton1 () {cout << _t ("destroy mySingleton1") << endl;}

PUBLIC:

Void dosomething () {cout << _t ("do something here in mysingleton1) << endl;}

}

Template

T & GetInstanceRef () // Returns a reference to the global unique object

{

Static T_INSTANCE;

Return_INSTANCE;

}

Template

T * getInstancePtr () // Returns a pointer to the global unique object

{

Return & GetInstanceRef ();

}

In the above code, Mysingleton1 is a class that requires a single instantiated class. The following template function Template T & GetInstanceRef () returns a reference to the unique instance of the class (static variable _instance), and the other template function calls it returns the pointer to the instance. We can notice the following points:

1. The constructor of MySingleton1 is private, preventing programmers from constructing an instance of it.

2. Similarly, copy constructor mysingleton1 (const mySingleton1 &) is also declared as private. 3. Global Template Functions Template T & GetInstanceRef () is a friend of MySingleton1. Because MySingleton1 constructor has declared as private, in order to make GetInstanceref to construct static variables smoothly _instance, we have to declare it as MySingleton1's friend function.

In this way, our class MySingleton1 has a Singleton feature, and the global access point is two template functions. The test code is as follows:

MYSINGLETON1 * myObj1;

MYOBJ1 = GetInstancePtr ();

MYOBJ1-> DOSMETHING ();

GetInstanceRef (). DOSMETHING ();

Below we analyze the disadvantages of this implementation. Since the template function getInstanceRef is passed by specialization, it is necessary to access MySingleton1, and its statement must be declared in the mysingleton1) (distinguish between declarations and implementation), which is not in line with us. Although it is relatively good in other respects, this shortcoming has made me no longer want to use it. To see the second can be implemented actually.

Second, based on template class realization

The basic idea of ​​this implementation is to make a class to be responsible for providing generation and access from Singleton objects. Since it is to construct a Singleton object, let it become a friend. Let's take a look at the implementation code:

Template

Class Singletonwraper

{

PUBLIC:

Static T & GetInstanceRef ()

{

Static T_INSTANCE;

Return_INSTANCE;

}

Static Const T & GetInstanceConst ()

{

Return getInstanceRef ();

}

Static T * getInstancePtr ()

{

Return & GetInstanceRef ();

}

}

#define define_singleton (classname); /

PUBLIC: /

Friend Class Singletonwraper ; /

Typedef class singleletonwraper Singletonwraper; /

Typedef Singletonwraper Singletoninterface; /

PRIVATE: /

ClassName (const classname &) {} /

ClassName & Operator = (const classname &) /

{/

Return Singletoninterface :: getInstanceRef (); /

} /

Private: // end of define define_singleton (classname);

Class mysingleton2

{

Define_nsingleton (Mysingleton2);

Private:

Mysingleton2 () {cout << _t ("construct mySingleton2") << endl;}

PUBLIC:

~ Mysingleton2 () {cout << _t ("destroy mySingleton2") << endl;} public:

Void Dosomething () {cout << _t ("do something here in mysingleton2) << endl;}

}

Let's take a look at the Singletonwraper class, which provides three static functions to get access to the Singleton object. Look at the following macro, its role is to declare the friend and define the access point of the two Singleton objects, and it also overloads the copy constructor to make the Singleton object that the access is always by GetInstanceref. An example of inert generation. We can see that using this SingletonWraper to packaging the Singleton class has become very simple. We only need to declare a statement define_singleton (MySingleton2) in the class that needs Singleton; But this still has some prerequisites, such as constructor (including copy constructor) private and destructive functions. The test code is as follows:

Mysingleton2 * myobj2;

MYOBJ2 = Singletonwraper :: getInstancePtr ();

MYOBJ2-> DOSMETHING ();

Mysingleton2 :: Singletonterface :: getInstanceref (). Dosomething ();

Third, based on the realization of its own static member function

This implementation is not complex than the previous implementation, the opposite, is simpler. The idea is to start from the class you want to achieve Singleton, implement its static member function to provide global instance access. This example is also in a static member function that occurs inside it, so we don't have to use friends to provide additional access. And, we have no use of any template. code show as below:

#define declare_singleton (classname); /

PUBLIC: /

Static ClassName & GetInstanceRef () /

{/

Static classname _INSTANCE; /

Return_INSTANCE; /

} /

Static const classname & getInstanceconst () /

{/

Return getInstanceRef (); /

} /

Static classname * getInstancePtr () /

{/

Return & getInstanceRef (); /

} /

PRIVATE: /

ClassName (const classname &) {} /

ClassName & Operator = (const classname &) /

{/

Return getInstanceRef (); /

} /

PRIVATE: /

Static Void Operator Delete (Void * P, SIZE_T N) /

{/

; / * Hey, don't do anything.

But note that the destructor has been executed.

But the object does not really unload from memory. * / /

} // end of define declare_singleton (classname);

Class mysingleton3

{

Declare_singleton (Mysingleton3);

Private:

Mysingleton3 () {cout << _t ("construct mySingleton3) << endl;} public:

~ Mysingleton3 () {cout << _t ("destroy mySingleton3) << endl;}

PUBLIC:

Void Dosomething () {cout << _t ("do something here in mysingleton3) << endl;}

}

Implementing the code of Singleton is a macro definition, and use it to make a class owned Singleton property just call this macro. From use, it should be the simplest, see the test code below:

MYSINGLETON3 * MyObj3 = mysingleton3 :: getInstancePtr ();

MYOBJ3-> DOSMETHING ();

Delete myobj3;

Mysingleton3 :: getInstanceRef (). DOSMETHING ();

In contrast the test code here and the previous one, it can be found that this method is also the simplest during use. The first three methods are created in the stack space, and the destruction of the object is on the scope boundary. Careful readers may have discovered problems. If we get the object's pointer, give it to Delete, it will definitely appear. For the first two implementations, we do not overload the Delete operator, and the pointer will not be available after Delete. For the third implementation, we have the DELETE overload function, which prevents the true uninstallation of the object. But before executing the Delete function, the destructor has executed because the global delete operation first calls the class destructor, then call the class DELETE overload operator function. The assembly code clearly shows this:

MySingleton3 :: `Scalar Deleting Destructor:

00412270 Push EBP

......

0041228D MOV ECX, DWORD PTR [EBP-4]

0041290 Call @ ilt 175 (Mysingleton3 :: ~ mysingleton3) (004010B4)

0041295 MOV Eax, DWORD PTR [EBP 8]

00412298 and Eax, 1

0041229B Test Eax, EAX

0041229D JE MYSINGLETON3 :: `Scalar Deleting Destructor ' 3DH (004122AD)

0041229F Push 4

004122A1 MOV ECX, DWORD PTR [EBP-4]

004122A4 PUSH ECX

004122A5 Call @ ilt 70 (Mysingleton3 :: Operator Delete) (0040104B)

004122AA Add ESP, 8

......

The global unique object of singleton in the three implementations of the previous implementation is automatically created (inert initialization) and automatically destroyed (on the scope boundate), and the programmer will be wrong without performing the delete operation, this is better than we execute in the program As the following code.

INT i (0);

INT * P = & I;

Delete P;

Obviously, this is not allowed. The best way to handle this is to throw an exception when Delete is, because we don't allow programmers to use the delete operation here. Consider the following code: Static Void Operator Delete (Void * P, Size_t N) /

{throw -1;

Corresponding test code is changed to:

Try {delete myobj3; / * Try to uninstall the object * /}

Catch (...) {cout << _t ("Your Object Cannot Be deleted.") << Endl; / * failed * /}

Fourth, the implementation of "Design Patterns" and its improvements

On the "Design Patterns --- Elements of Reusable Object-Oriented Software" (English) discussed the Singleton mode, there is also an implementation, but there is a serious flaw: there is no destruction of the object. The following is the Sample code it gives:

Class Mazefactory {

PUBLIC:

Static Mazefactory * instance ();

// EXISTING INTERFACE GoES Here

protected:

MazeFactory ();

Private:

Static Mazefactory * _INSTANCE;

}

MazeFactory * mazefactory :: _ instance = 0;

MazeFactory * mazefactory :: instance () {

IF (_Instance == 0) {

New Mazefactory;

}

Return_INSTANCE;

}

First analyze its implementation strategy. The first is the constructor access restriction (protected) and then declare a static object pointer, initialization of the pointer (or instantiation of the class) is in the static member function instance. Here it does not have the corresponding object uninstall code, however, the object generated in the free storage space (heap space) is not automatically uninstalled. Therefore, after improvement, I got the following code.

Class mysingleton4

{

Private:

Mysingleton4 () {cout << _t ("construct mySingleton4") << endl;} // constructor private ~ mysingleton4 () {cout << _t ("deStroy Mysingleton4") << Endl;} // destructor Where can I?

Static mysingleton4 * _INSTANCE;

PUBLIC:

Static Mysingleton4 & GetInstanceRef ()

{

IF (_Instance == 0)

_INSTANCE = New Mysingleton4;

Return * _INSTANCE;

}

Static mysingleton4 * getInstancePtr ()

{

Return & GetInstanceRef ();

}

Static ReleaseInstance ()

{

IF (_Instance! = 0)

{

Delete _INSTANCE;

_INSTANCE = 0;

}

}

PUBLIC:

Void dosomething () {cout << _t ("do something here in mysingleton4) << endl;}};

Mysingleton4 * mysingleton4 :: _ instance = 0; // Singleton object initialization

Static Class DestructHelper // Auxiliary class for uninstalling MySingleton4 objects

{

PUBLIC:

~ Destructhelper () {mysingleton4 :: ReleaseInstance ();}

} DESTRUCTHELPERINSTANCE; // Auxiliary class still instance

The unique improvement in the code is to increase the static function of the release object ReleaseInstance. Note that in this function _instance! = 0 judgment and later _instance = 0 are essential because the function ReleaseInstance may be repeatedly called. After uninstalling the object points to the pointer, it will be set to 0 is a very good program habit, which avoids the appearance of "wild pointers", which is usually very dangerous. The next class DEStructHelper is used to assist the Singleton object to uninstall MySingleton4, and we call the mysingleton4 :: release in its public destructory function to complete the work. A global static instance of DESTRUCTHELPER DESTRUCTHELPERINSTANCE will uninstall it when appropriate, which guarantees the destructhelper () to be called to uninstall the Singleton object of MySingleton4. When using, we can call ReleaseInstance () to uninstall the object, then call GetInstanceRef () to get a new object. If we don't want to do this, the destructhelperinstance destructor will ensure that the objects in the heap are automatically uninstalled. Such a processing method adds more flexibility.

However, the shortcomings are still there. The main regret is the reuse of the code. First, the two implementations are based on template technology. The code can be used and convenient. Although there is no use of template technology, the use of macro definitions can also be a good reuse of code. The final implementation is a bit slightly difficult to reuse. The best solution I can think is still using macros (although I also oppose the use of macros in C ), a process for defining the mysingleton4 class, one for generating a static instance of unloading auxiliary class. The second macro is the taste of the same work, but it also generates a static variable associated with the type. For specific code, please see the source list.

Five, conclude

This article all code is all passed in VC6.0.

This paper proposed only a few simple implementations written by the author, but it is already possible to meet the needs of most applications. For more complex implementation, please refer to http://www.hoversoft.net/devinfo/0205doc/20singleton/index.htm. This article author "fuel tank": vcvbjava@yahoo.com. Welcome everyone to discuss in order to make progress.

This article author Zhang Youbang is Microsoft Certified Expert, and now in Hunan Changsha High-tech Development Zone Chuangzhi Software Park Chui Digital Technology Co., Ltd. is engaged in development. You can contact him through vcvbjava@yahoo.com.

转载请注明原文地址:https://www.9cbs.com/read-650.html

New Post(0)