C ++ Implementation of Singleton

Source: Internet
Author: User

Singleton

The Singleton pattern: ensures a class has only one instance, and provides a global point of access to it.
There is only one instance class. The following are several considerations:
First, you must call the constructor to generate a class instance. To prevent user declaration or a new class instance, we can set the class constructor to protected or private, so user declaration or new cannot be compiled. Of course, delete is also not allowed. It is also declared as protected or private.
But where can we create a class instance? Although you cannot create class objects externally, you can call constructor internally to create class instances. We provide a method getinstance () for the class exterior (). It creates a class instance when there is no class instance and returns the instance (pointer or reference); when there is an instance, it returns the existing instance directly. In this case, we need to save the instance pointer for getinstance () usage. The pointer should also be defined on the class. Otherwise, how can we assign a value to this member variable before there is no class instance? In this case, the first version is:
Class Singleton
{
Public:
Static Singleton & instance ()
{
If (! Si_instance)
{
Si_instance = new Singleton;
}
Return * si_instance;
}
PRIVATE:
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static Singleton * si_instance; // declare a static data member
};
Singleton * singleton: si_instance = 0; // defines and initializes global storage, locally applied static data members
Static is used outside the class:
Static and extern are both variable Storage types. They are declared static changes and cannot be referenced by other files (target files, extern can be referenced by this file and other files (the target file ). You cannot use them to declare the same change at the same time. They are stored in the static memory of the program ).
Here, static can be used for the declaration of local variation. At this time, when the call function is performed, the static local variation value is not initialized by the row, that is, the value after the last reference is retained.
Extern is the statement of default.
Static local changes are not recommended.
The usage of the software is also related to the compiler. The preceding description is based on the C Complier baseline defined by ansi c.
The CPP file generates the target file. Obviously, the static or extern variable in the header file of the include CPP file is still valid in CPP (not only in the header file ). When linking, static variables can only be referenced in this OBJ, while extern can be referenced in all OBJ (this may lead to duplicate names and a redefinition error, so the static keyword is introduced ). Static global functions are also used to restrict the global functions in this OBJ file.
Static is used in class definition:
A class is a type definition. The definition of all members in the class is a declaration, indicating the internal structure of the class. Only the code that generates the class instance needs to allocate storage space for the class members, either on the stack or in the heap.
To process variables defined on the class, we use static to modify the member. In this case, the static semantics indicates that the member is an extern type global variable. Since the class definition is declarative, for example, to declare static int I in class fun, and I is not defined (to generate an instance), we need to define and initialize fun outside the class :: i, which is why static variables in the class must be initialized and must be initialized outside the class.
However, this brings about an additional problem, that is, if multiple CPP files contain a fun-like definition file, then for fun :: the I definition and initialization statement will appear in multiple CPP files, and the link will fail!
This is why we need to distinguish the header file from the CPP file. We will write the "Declaration" in the header file and the "Definition" (that is, the code that will generate the instance) in the CPP file. Because we never include the CPP file, we define fun :: I only once !!! This principle also applies to class methods. If the class method F () is implemented outside the class, it should also be written in CPP. Otherwise, an error occurs when the header file is referenced by multiple CPP files. If you directly write the member function body in the class, there is no problem. Although multiple OBJ files containing the Class header file generate fun: F, the scope defined by this function will not exceed the code block of this class, so it will not conflict. During compilation, multiple copies of the function are actually generated, and each copy only works locally. The copies used in the link are written into the EXE file and even processed as inline functions. In contrast, if you write fun: F () in the class implementation CPP file, then only one fun: F () code is generated during compilation, it seems that compilation takes less time.
When a class member function is modified using static, it indicates that it is a method defined on the class. The function does not store the class. In this hot summer, you can call it without passing the this pointer, that is, you can reference the object through the class name. Of course, if you create a class object, you can also reference it through the object. A common member function needs to be used through the this pointer (actually passing the this pointer to that function)
Summary: In the definition code (code outside the class), static ensures that the object lifetime is the whole process of running the program. It is defined in the Code block and the scope is in the code block, outside the code block (globally defined), the scope is the OBJ file
In declarative code (in the class), static ensures that the lifetime of the modified object is the whole process of program execution, and the scope is global, because it is a declarative, not a definition, therefore, for member variables, global members need to be defined outside the class. For member functions, both the external and internal definitions are acceptable, because static only means that this pointer is not passed to the function.
This version has a problem: the object is created, but where can I delete this object? We are in the new object inside the class, and there is no new external, so the external should not be deleted. Si_instance is a static variable used to save pointers. It is actually a global variable, that is, it exists during process loading and is destroyed only when the process exits (main () after the program ends ). Can I delete an object when si_instance is destroyed? Yes. If we use auto_ptr of the standard library, but to enable auto_ptr to delete the object created, we need to set it as a friend Meta class of singleton and get the following testable code:
// Singleton. h
# Include <memory>
# Include <iostream>
Using namespace STD;
Class Singleton
{
Friend class auto_ptr <Singleton>;

Public:
Static Singleton * instance ()
{
If (! Si_instance.get ())
{
Si_instance.reset (New Singleton );
}
Return si_instance.get ();
}

PRIVATE:
~ Singleton () {cout <"delete Singleton";} // disable External Delete
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static auto_ptr <Singleton> si_instance;
};
// Singleton. cpp
# Include "singleton. h"
Auto_ptr <Singleton> singleton: si_instance (null); // static variables must be initialized outside the class.
// Test. cpp
# Include "singleton. h"
Void main ()
{
Singleton * PS = singleton: instance ();
}
In the case of a single thread, the above Code is ready, but if there are multiple threads, then at the beginning, there are two threads calling the instance, and the pointer is found to be empty, therefore, when both threads create an object, an error occurs, that is, the preceding Code thread is not safe. How to synchronize threads? Using criticalsection is a good solution. You can use the criticalsection of Windows API or the ccriticalsection of MFC. The latter is easier to use, and Initialization is not required. You can use the lock and Unlock methods for ease of use. However, you need to use MFC. According to the Occam Razor principle, no additional concepts can be introduced. Therefore, the former is used. But the problem is:
Where can I define criticalsection? It is better to have a local scope and survive during program execution. Considering that it is closely related to Singleton, it is most appropriate to declare it as a static member of Singleton, define it outside the class (Singleton. CPP)
In addition, criticalsection needs to call initiallize () for initialization. Where can I execute this statement? Obviously not in the main () function, the customer should not care about Singleton's thread security implementation; in the singleton constructor? No. The constructor is called after the singleton object is created. Before that, criticalsection must work! One feasible method is to let Singleton have a static class member of the cresguard type. Should this static class member be initialized outside the class? So will its constructor be called? Well, initiallize () criticalsection in its constructor. It seems that there is no problem. In Singleton, define static criticalsection and static cresguard, and then initialize criticalsection in the cresguard constructor. However, what if the constructor calls the cresguard during initialization and the criticalsection is not created yet? This will cause the call of initialize () to fail. They are all local functions and survive throughout the process. programs cannot depend on their creation sequence. Put the criticalsection in cresguard as a member. The problem is that the static member criticalsection of cresguard must be initialized outside the cresguard class and cannot be used in constructors! It should be defined as non-static, because cresguard is static, and criticalsection does not need to be defined as static. The following code is obtained:
// Cresguard. h
# Include <windows. h>
Class cresguard
{
PRIVATE:
Critical_section Cs; // critical section
Public:
Cresguard () {initializecriticalsection (& CS );}
~ Cresguard () {deletecriticalsection (& CS );}
Void lock () {entercriticalsection (& CS );}
Void unlock () {leavecriticalsection (& CS );}
};
// Singleton. h
# Include "cresguard. H"
# Include <memory>
# Include <iostream>
Using namespace STD;
Class Singleton
{
Friend class auto_ptr <Singleton>;
Public:
Static cresguard;
Public:
Static Singleton * instance ()
{
Cresguard. Lock ();
If (! Si_instance.get ())
{
Si_instance.reset (New Singleton );
}
Cresguard. Unlock ();
Return si_instance.get ();
}
PRIVATE:
~ Singleton () {cout <"delete Singleton";} // used for testing
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static auto_ptr <Singleton> si_instance;
};
// Test. cpp
# Include "Singleton. H"
Void main ()
{
Singleton * PS = singleton: instance ();
}
The Code thread is secure now. However, if a thread enters the critical section code and encounters an exception, we cannot guarantee that the unlock () will be executed, and the wait will be suspended when other threads call the instance. One way is to use the try .. catch statement to ensure that unlock () is executed. A better way is that all the variable systems on the stack will be destroyed when they exit the code block. If an automatic variable is used, the variable will be locked when it is created, and the unlock will be used when it is destroyed, then try different... Catch statement. We treat the type of this automatic variable as the class type, and lock and unlock in its constructor and destructor. If we regard it as a lock, the lock is defined at the beginning of the critical code block. The lock is automatically destroyed and unlocked when the critical code block ends (included in. The lock type definition is simply put in cresguard to make it a whole. In terms of name, we handle the lock cresguard, that is, the class name is lockcresguard. The following code is obtained:
// Cresguard. h
# Include <windows. h>
Class cresguard
{
PRIVATE:
Critical_section Cs; // critical section
Void lock () {entercriticalsection (& CS );}
Void unlock () {leavecriticalsection (& CS );}
Public:
Cresguard () {initializecriticalsection (& CS );}
~ Cresguard () {deletecriticalsection (& CS );}
// Nested class, construct the destructor to call the cresguard object. Lock/unlock
Class lockcresguard
{
PRIVATE:
Cresguard & _ CRES;
Public:
Lockcresguard (cresguard & CRES): _ CRES (CRES) {_ Cres. Lock ();}
~ Lockcresguard () {_ Cres. Unlock ();}
};
};
// Singleton. h
# Include "cresguard. H"
# Include <memory>
# Include <iostream>
Using namespace STD;
Class Singleton
{
Friend class auto_ptr <Singleton>;
PRIVATE:
Static cresguard;
Public:
Public:
Static Singleton * instance ()
{
// The lock variable is declared below. cresguard. Lock is called during initialization.
Cresguard: lockcresguard lock (cresguard );
If (! Si_instance.get ())
{
Si_instance.reset (New Singleton );
}
Return si_instance.get ();
} // Lock the Destructor and call cresguard. Unlock.
PRIVATE:
~ Singleton () {cout <"delete Singleton";} // used for testing
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static auto_ptr <Singleton> si_instance;
};
// Singleton. cpp
# Include "Singleton. H"
Cresguard singleton: cresguard;
Auto_ptr <Singleton> singleton: si_instance (null); // static variables must be initialized outside the class.
// Test. cpp
# Include "Singleton. H"
Void main ()
{
Singleton * PS = singleton: instance ();
// PS = new Singleton; // unable to execute, construct private
// Delete pS; // unable to execute, private structure
}
Basically, the thread needs to be synchronized every time the singleton instance is called. It can be said that it is very bad because it will affect the performance, this function is called multiple times or even frequently by multiple threads. We can use double_check lock. That is to say, we should first check whether si_instance.get () is null, not empty, and return directly, the synchronization thread (locking) is empty. If the thread is empty, the system returns a result. The advantage of doing so is that after Singleton creates the first instance, the subsequent calls do not synchronize threads, and of course do not create lock automatic variables, and the program speed is also improved. Double check is used to improve performance and thread security. It depends on the critical section and is irrelevant. The modification code is as follows:
Static Singleton * instance ()
{

If (! Si_instance.get ())
{// The lock variable is declared below. cresguard. Lock is called during initialization.
Cresguard: lockcresguard lock (cresguard );
If (! Si_instance.get ())
Si_instance.reset (New Singleton );
} // Lock the Destructor and call cresguard. Unlock.
Return si_instance.get ();
}
After the class is written, how can it be used in general?
First, it is easy to rewrite Singleton to return a template for a T-type instance. You only need to modify the template in several locations.
So I have a singleton that returns the T-type instance. Can I construct a single-instance class? Of course you can, as long:
Define a new class and set its constructor and destructor to private or protected
Set Singleton <class> and auto_ptr <class> to the element of the class. The former must call the constructor of the class, and the latter must call the destructor of the class.
// Singleton. h
# Include "cresguard. H"
# Include <memory>
# Include <iostream>
Using namespace STD;
Template <class T>
Class Singleton
{
Friend class auto_ptr <t>;
PRIVATE:
Static cresguard;
Public:
Public:
Static T * instance ()
{

If (! Si_instance.get ())
{// The lock variable is declared below. cresguard. Lock is called during initialization.
Cresguard: lockcresguard lock (cresguard );
If (! Si_instance.get ())
Si_instance.reset (new t );
} // Lock the Destructor and call cresguard. Unlock.
Return si_instance.get ();
}
PRIVATE:
~ Singleton () {cout <"delete Singleton";} // used for testing
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static auto_ptr <t> si_instance;
};
Template <class T>
Cresguard Singleton <t >:: cresguard;
Template <class T>
Auto_ptr <t> Singleton <t>: si_instance (null); // static variables must be initialized outside the class.
Note that the static variables are moved to the header file. Because the template represents the type, the template is instantiated as the type, because if it is placed in the original CPP file, during compilation, I don't know what type to use to bring it into T, because it is to compile Singleton. H + Singleton. CPP, the compiler does not know which specific class is used into T; instead, it is compiling myclass. CPP + Singleton. h, and no Singleton. CPP, the static variable is treated as an external symbol, and an error occurs during the link. Currently, all VC templates are compiled in the form of inclusion, that is, the template definition must be included in the compilation, not just the declaration.
Other related code:
// Myclass. h
# Include "Singleton. H"
Class myclass
{
Friend class Singleton <myclass>;
Friend class auto_ptr <myclass>;
PRIVATE:
Myclass () {cout <"construct myclass/N ";}
~ Myclass () {cout <"destruct myclass/N ";}
Public:
Static myclass * instance () {return Singleton <myclass >:: instance ();}
};
// Test. cpp
# Include "myclass. H"
Void main ()
{

Myclass * pmyclass = myclass: instance ();
Myclass * pmyclass1 = myclass: instance ();
Myclass * pmyclass2 = myclass: instance ();

}
However, it is still difficult to write it. Use macros to sort it out:
// Singleton. h
# Include "cresguard. H"
# Include <memory>
# Include <iostream>
Using namespace STD;
Template <class T>
Class Singleton
{
Friend class auto_ptr <t>;
PRIVATE:
Static cresguard;
Public:
Public:
Static T * instance ()
{

If (! Si_instance.get ())
{// The lock variable is declared below. cresguard. Lock is called during initialization.
Cresguard: lockcresguard lock (cresguard );
If (! Si_instance.get ())
Si_instance.reset (new t );
} // Lock the Destructor and call cresguard. Unlock.
Return si_instance.get ();
}
PRIVATE:
~ Singleton () {cout <"delete Singleton";} // used for testing
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static auto_ptr <t> si_instance;
};
Template <class T>
Cresguard Singleton <t >:: cresguard;
Template <class T>
Auto_ptr <t> Singleton <t>: si_instance (null); // static variables must be initialized outside the class.
// The macro definition is as follows. Use/to connect multiple rows to represent a row.
# Define declare_singleton (type )/
Friend class Singleton <type> ;/
Friend class auto_ptr <type> ;/
PRIVATE :/
Type () {cout <"construct type/N ";}/
~ Type () {cout <"destruct type/N ";}/
Public :/
Static type * instance () {return Singleton <type >:: instance ();}

Usage:
# Include "Singleton. H"
Class myclass
{
Declare_singleton (myclass)
... ...
};

Every Singleton may have its own personality. When constructing and analyzing the structure, there may be additional operations. We will extend it to the following two pure virtual functions, each Singleton class completes the final code:
// Cresguard. h
# Include <windows. h>
Class cresguard
{
PRIVATE:
Critical_section Cs; // critical section
Void lock () {entercriticalsection (& CS );}
Void unlock () {leavecriticalsection (& CS );}
Public:
Cresguard () {initializecriticalsection (& CS );}
~ Cresguard () {deletecriticalsection (& CS );}
// Nested class, construct the destructor to call the cresguard object. Lock/unlock
Class lockcresguard
{
PRIVATE:
Cresguard & _ CRES;
Public:
Lockcresguard (cresguard & CRES): _ CRES (CRES) {_ Cres. Lock ();}
~ Lockcresguard () {_ Cres. Unlock ();}
};
};
// Singleton. h
# Include "cresguard. H"
# Include <memory>
# Include <iostream>
Using namespace STD;
Template <class T>
Class Singleton
{
Friend class auto_ptr <t>;
PRIVATE:
Static cresguard;
Public:
Public:
Static T * instance ()
{

If (! Si_instance.get ())
{// The lock variable is declared below. cresguard. Lock is called during initialization.
Cresguard: lockcresguard lock (cresguard );
If (! Si_instance.get ())
Si_instance.reset (new t );
} // Lock the Destructor and call cresguard. Unlock.
Return si_instance.get ();
}
PRIVATE:
~ Singleton () {cout <"delete Singleton";} // used for testing
// Disable External constructor calls
Singleton (){}
// Object static pointer, used to save the created object
Static auto_ptr <t> si_instance;
};
Template <class T>
Cresguard Singleton <t >:: cresguard;
Template <class T>
Auto_ptr <t> Singleton <t>: si_instance (null); // static variables must be initialized outside the class.

# Define declare_singleton (type )/
Friend class Singleton <type> ;/
Friend class auto_ptr <type> ;/
Protected :/
PRIVATE :/
Type () {cout <"construct type/N"; initialinstance ();}/
~ Type () {cout <"destruct type/N"; disposeinstance ();}/
Public :/
Static type * instance () {return Singleton <type >:: instance ();}
// Myclass. h
# Include "Singleton. H"
Class myclass
{
// The following lines must have
Declare_singleton (myclass)
Protected:
Virtual void initialinstance () {cout <"initialinstance ";}
Virtual void disposeinstance () {cout <"disposeinstance ";}
// You can write what you want to write ......
};
// Test. cpp
# Include "myclass. H"
Void main ()
{

// PS = new Singleton; // unable to execute, construct private
// Delete pS; // unable to execute, private structure
Myclass * pmyclass = myclass: instance ();
Myclass * pmyclass1 = myclass: instance ();
Myclass * pmyclass2 = myclass: instance ();

}

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.