Two weeks before, one of my colleagues asked me, how to manage the instance destruction of singleton class? Then I thought about it, because in most of the cases, when the program terminates, it will destruct the memory allocated. So never I bothered about it. Frankly speaking I hadn’t an opportunity to write a singleton class in any of the official projects I handled. So my experiment with single tone class had an end when I see the single instance and restriction in creating more objects.
Now in my project, there a lot of singleton class need to be implemented. So at the time of design, obviously, the question came into matter. How we could cleanup the instance.
Our program run till the system ends and even if we need that memory for till the system stops, as best designed class, all these singleton classes should have a cleanup operation. As a reference, we have checked the “Design Patterns -Elements of Reusable Object-Oriented software” by Erich Gamma’s book. But in that book it’s not saying anything about cleanup actions. It’s saying how to handle multiple instances and all.
Ok I said a lot about Singleton class for those people new to this concept, let me explain it in short words.
A Singleton class is
• Main objective of a singleton class is to control the number of instances of a class within a program.
• This can be accomplished by hiding the constructor (make it as private) of the class and destructor.
o If the constructor is private, none can instantiate it.
o Making destructor private to control the destruction of the instance returning (as pointer). Unexpected delete of the instance will prone the program to crash.
How we could manage?
First of all we need static function to return the static object we are creating (remember other functions and classes can’t make an object). We can use a number of approaches to return the instance.
1. Return a static object created within the function
2. Allocate memory for the object at heap on calling it first time returns this allocated pointer on function returns.
3. If there are fixed number of instances (more than one) we can keep a map with unique key and these instances, dynamically created etc…
In the above approaches, the first method is safe but there’s not control over the memory (to delete after use) of the instance.
Now what I’m going to explain is about the second type of instance handling.
The following sample illustrates creating an instance using second method.
CSingleton* CSingleton::m_pszInstance = NULL;
static CSingleton* _Instance()
{
if (NULL == CSingleton::m_pszInstance)
CSingleton::m_pszInstance = new CSingleton;
}
Creating the instance(s) is not a big deal but cleanup need some attention.
In this approach the cleanup requirement comes into picture. How we could accomplish this?
Yes, as you think add a static function to cleanup (delete) the instance. For e.g. add a function static void PrepareToDie(); to cleanup the instance.
What we can do inside this function?
static void _PrepareToDie()
{
if (NULL != CSingleton::m_pszInstance)
delete m_pszInstance;
}
But this method has some problems. We can call _Instance from many classes and functions (especially thread functions). If a function call instance _PrepareToDie this will delete the pointer make other functions crash since their local pointers getting invalid. So it’s our duty to keep the reference count as we do in the COM. When functions call _Instance function to get the instance, increment the reference count, when all the functions call delete, decrement the reference count. When it becomes zero, delete the instance.
The following code illustrates the concept.
class CSingleton
{
// make ctor and dtor private
private:
CSingleton(){}
~CSingleton(){}
static CSingleton* m_pszInstance;
static int m_nReference;
public:
static CSingleton* _Instance()
{
if (NULL == CSingleton::m_pszInstance) // allocate the memory
CSingleton::m_pszInstance = new CSingleton;
m_nReference++; // increment the count
return m_pszInstance;
}
static void _PrepareToDie()
{
m_nReference--; // decrement the count
if ((m_nReference <=0) &&
(NULL != CSingleton::m_pszInstance))
{
delete m_pszInstance;
m_pszInstance = NULL;
}
}};
CSingleton* CSingleton::m_pszInstance = NULL;
int CSingleton::m_nReference = 0;
int main()
{
CSingleton* pInstance = CSingleton::_Instance();
pInstance = CSingleton::_Instance();
pInstance = CSingleton::_Instance();
CSingleton::_PrepareToDie();
CSingleton::_PrepareToDie();
CSingleton::_PrepareToDie();
return 0;
}
This method is something like new and delete. Because we have to call the cleanup function if we called the instance method else, memory leak occurs in your program. Suppose in the above example, if called prepare to die only once, there will be memory leak. Because all these are depends on count which is maintaining. Sometimes we will call functions repeatedly if we don’t call cleanup function’s it will make memory leak.
Anyway I haven’t made this class thread safe. Suggest me if there’s better idea to control cleanup of the instance.