Search for Robust Singleton Design Pattern

Discussion in 'C++' started by asadullah.ansari, Jan 19, 2009.

  1. asadullah.ansari

    asadullah.ansari TechCake

    Joined:
    Jan 9, 2008
    Messages:
    356
    Likes Received:
    14
    Trophy Points:
    0
    Occupation:
    Developer
    Location:
    NOIDA

    Background



    Normally most of software Engineer uses singleton design pattern in daily coding life. It's very simple , vast uses but still i saw in many softwares which is not handled proper in C++ based products/projects. One thing more you can'nt design singleton as perfectly behaved singleton. But here approch and solution for how to make perfact singleton class in C++.

    Why Singleton: To maintain object as single instance which is used by more than one user.

    Details & Step by Step Improvement



    Step 1: In first sight, singleton can be create as
    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
     
          private:
             static Singleton *instSingleton_mp;
             Singleton(){ ; }
    };
    
    //Definition
    Singleton* Singleton::InstSingleton_mp = NULL;
    Singleton* Singleton::CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
          }
          return InstSingleton_mp;
    }
    Now Can you say Is it complete design of Singleton..I hope more than 99% people will say NO !!! We have to take care about copy constructor .We should put copy constructor in private section also because any one can create a new object based on already created singleton object. Okay lets go ahead with step 2.

    Step 2: Adding Copy Constructor in private section

    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
             
          private:
             static Singleton *instSingleton_mp;
             Singleton(){ ; }
             Singleton(const Singleton &) { ; }
    };
    
    //Definition
    Singleton* Singleton::InstSingleton_mp = NULL;
    Singleton* Singleton::CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
          }
          return InstSingleton_mp;
    }

    Now Again Some people (Now i hope 95%) will say it's not complete design, we should take of memory management. Okay Now we will go for that.

    Step 3: Adding memory management code

    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
              ~Singleton();
             
          private:
             static Singleton *instSingleton_mp;
             Singleton(){ ; }
             Singleton(const Singleton &) { ; }
    };
    
    //Definition
    Singleton* Singleton :: InstSingleton_mp = NULL;
    Singleton* Singleton :: CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
          }
          return InstSingleton_mp;
    }
    Singleton :: ~ Singleton()
    {
         detele  InstSingleton_mp;
    }
    Again problem will be now infinite time destructor will be called by line delete operator.
    Okay It can be done by little bit changes.

    Step 4: Adding memory management code ( Continued )

    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
              ~Singleton();
              void DestroyMe();
          private:
             static Singleton *instSingleton_mp;
             Singleton(){ ; }
             Singleton(const Singleton &) { ; }
    };
    
    //Definition
    Singleton* Singleton :: InstSingleton_mp = NULL;
    Singleton* Singleton :: CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
          }
          return InstSingleton_mp;
    }
    Singleton :: DestroyMe()
    {
          if( NULL ! =  InstSingleton_mp )
         {
                 detele  InstSingleton_mp;
                 InstSingleton_mp=NULL;
         }
    }

    Now I hope 90% people will say again it has problem, suppose lot of users using this application then ... Suppose X guy uses this object and release it and other guy Y still wants to use this object then problem.....Okay lets see in next step.

    Step 5: Introducing reference counts (In our software a class is available for it)

    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
              ~Singleton();
              void DestroyMe();
          private:
             static Singleton *instSingleton_mp;
             static int refCount;
             Singleton(){ ; }
             Singleton(const Singleton &) { ; }
    };
    
    //Definition
    Singleton* Singleton :: InstSingleton_mp = NULL;
    int Singleton :: refCount = 0;
    Singleton* Singleton :: CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
               ++refCount;
          }
          return InstSingleton_mp;
    }
    Singleton :: DestroyMe()
    {
          if( NULL ! =  InstSingleton_mp && 0 = = refCount)
         {
                 detele  InstSingleton_mp;
                 --refCount;
                InstSingleton_mp=NULL;
         }
    }

    Now hope people will say it's perfect solution. But still i hope 80% people will say it has problem ...Suppose One user calling DestroyMe() function more than one times, lets say five times then no probs for a while( on that time refCount will be -4 ) but problem will be raised when next user is calling createSingleton() (refCount will be -3) and calking DestroyMe() in which destructor itself will not be called. A liitle bit changes can be done.

    Code:
    Singleton :: DestroyMe()
    {
          if( NULL ! =  InstSingleton_mp && 0 = = refCount)
         {
                 detele  InstSingleton_mp;
                 if(0 < refCount)
                   --refCount;
                InstSingleton_mp=NULL;
         }
    }
    Now To make it complete singleton class some more addition is required. Like assignment operator is required or not...Offcourse it's required but it should be return same object.

    One more have putted destructor in public better if we will put in private because more constructor which is not in private can be putted in public section. To avoid this situation better if you put destructor in private section.

    Coplete design of singleton is as:

    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
              void DestroyMe();
              Singleton& operator= (const Singleton& );
          private:
             static Singleton *instSingleton_mp;
             static int refCount;
             Singleton(){ ; }
             Singleton(const Singleton &) { ; }
             ~Singleton();
    };
    
    //Definition
    Singleton* Singleton :: InstSingleton_mp = NULL;
    int Singleton :: refCount = 0;
    Singleton* Singleton :: CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
               ++refCount;
          }
          return InstSingleton_mp;
    }
    
    Singleton :: DestroyMe()
    {
          if( NULL ! =  InstSingleton_mp && 0 = = refCount)
         {
                 detele  InstSingleton_mp;
                 if(0 < refCount)
                   --refCount;
                InstSingleton_mp=NULL;
         }
    }
    Singleton& operator= (const Singleton& obj)
    {
       return *this;   
    }

    References



    1. GoF http://www.vincehuston.org/dp/
    2. http://hitechi.19.forumer.com/viewtopic.php?t=41
    3. Idea for Style of writing this article is taken from code guru.

    Note: Code written in this article is morally as designed purpose so dont expect it's runnable code. You may get compilation error.
     
  2. madlex

    madlex New Member

    Joined:
    Jun 6, 2008
    Messages:
    12
    Likes Received:
    3
    Trophy Points:
    0
    Occupation:
    Technical Project Manager
    Location:
    Bucharest,RO
    Sorry for telling you that, but your "robust" singleton design is closer to disastrous than perfect.

    First of all, the code itself doesn't reflect your description. Your intentions are good, but your code doesn't reflect your intentions.

    I know the Singleton design is supposing to have only one instance per "session", so I don't see the reason to dynamically allocate the object instead of pushing it onto the stack, and being destroyed automatically by the runtime library. This way no "memory management" will be needed for the Singleton design. Anyway I will skip that..

    Step 5 - Introducing reference counters is interesting

    The reference counting ... doesn't count.

    Code:
    Singleton* Singleton :: CreateSingleton()
    {
          if (NULL = = InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton();
               ++refCount;
          }
          return InstSingleton_mp;
    }
    
    if you increment the counter inside the condition if( ptr == NULL ) how will the counter increment when function is called. Will be "incremented" only when the function is called for the very first time.

    Code:
    Singleton :: DestroyMe()
    {
          if( NULL ! =  InstSingleton_mp && 0 = = refCount)
         {
                 detele  InstSingleton_mp;
                 if(0 < refCount)
                   --refCount;
                InstSingleton_mp=NULL;
         }
    }
    Now, here is the most interesting part. The reference counter will be decremented ONLY if it reaches 0.
    Basically, NEVER.
    Even if you will fix the IF condition clause, the reference is decremented only when the pointer is deleted and set to null. So only once per allocation.
    In this scenario, your counter isn't a ref-counter, is more a redundant variable.

    One more aspect that the "perfect" design is missing, is the thread safety of the singleton model.
    You force the user to use the singleton instance from one thread only. If the user uses this model in a multi-threaded application it may collapse under allocation and deallocation routines which are not designed to support that.

    Cheers
     
  3. asadullah.ansari

    asadullah.ansari TechCake

    Joined:
    Jan 9, 2008
    Messages:
    356
    Likes Received:
    14
    Trophy Points:
    0
    Occupation:
    Developer
    Location:
    NOIDA
    Yes!!! You are right...

    Correct code will be as:


    Step 5: Introducing reference counts (In our software a class is available for it)


    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
              ~Singleton();
              void DestroyMe();
          private:
             static Singleton *instSingleton_mp;
             static int refCount;
             Singleton(){ ; }
             Singleton(const Singleton &) { ; }
    };
    
    
    //Definition 
    Singleton* Singleton :: InstSingleton_mp = NULL;
    int Singleton :: refCount = 0;
    
    
    Singleton* Singleton :: CreateSingleton()
    {
          if (NULL == InstSingleton_mp)
          {
               InstSingleton_mp = new Singleton(); 
           }
    
          ++refCount;
          return InstSingleton_mp;
    }
    
    
    Singleton :: DestroyMe()
    {
    
          --refCount;
          if( NULL !=  InstSingleton_mp && 0 == refCount)
         { 
                 detele  InstSingleton_mp; 
                 InstSingleton_mp=NULL;
         }
    }
    
    

    Code:
    Singleton :: DestroyMe()
    {
    
          if(0 < refCount)
    
             --refCount;
          if( NULL !=  InstSingleton_mp && 0 == refCount)
         { 
                 detele  InstSingleton_mp; 
                 InstSingleton_mp=NULL;
         }
    }
    
    
     
  4. zamjad

    zamjad New Member

    Joined:
    Oct 7, 2008
    Messages:
    15
    Likes Received:
    1
    Trophy Points:
    0
    Even if we leave thread safty for a while then there are still few problems here.

    1. First you don't have to write empty body for copy ctor you can do the same thing with this

    Code:
    class Singleton
    {
          public:
              static Singleton * CreateSingleton();
              ~Singleton();
              void DestroyMe();
          private:
             static Singleton *instSingleton_mp;
             static int refCount;
             Singleton(){}
             Singleton(const Singleton &);
    };
    
    2. Second in your this code

    You are doing decrement in refCount variable only when it is less than zero (means you never did it).

    3. Third the static member variable of Singlton has different name in class and in body, so this code wont even compile

    4. Fourth return type is not defined for DestroyMe() function, so it wont compile. It should be like this

    Code:
    void Singleton :: DestroyMe()
    
    5. Fifth thers is no keyword like "detele". It should be delete.

    Code:
          if( NULL !=  InstSingleton_mp && 0 == refCount)
         { 
                 detele  InstSingleton_mp; 
                 InstSingleton_mp=NULL;
         }
    
    6. Sixth you didnt define the body of dtoc so you will get linking error. It should be something like this

    Code:
    ~Singleton() {};
    

    7. Seventh and probably the most important one is what will be the situation when someone write a code something like this

    Code:
    Singleton* pInstance1 = Singleton::CreateSingleton();
    Singleton* pInstance2 = pInstance1->CreateSingleton();
    
    delete pInstance1;
    delete pInstance2;
    
    Because you didn't make your dtor private therefore your reference counting and even your application will break here.

    Please at least compile the code before posting, so at least reader wont get at least compilation errors.
     
  5. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
  6. jayaraj_ev

    jayaraj_ev New Member

    Joined:
    Aug 29, 2007
    Messages:
    20
    Likes Received:
    2
    Trophy Points:
    0
    Occupation:
    Software engineer
    Location:
    Bangalore
    Hi,
    On your first Point mentioning that ....
    "I know the Singleton design is supposing to have only one instance per "session", so I don't see the reason to dynamically allocate the object instead of pushing it onto the stack, and being destroyed automatically by the runtime library. This way no "memory management" will be needed for the Singleton design. Anyway I will skip that.".
    As this class is singleton it will have only one instance...and How will you make sure that this having only one instance that is why we dynamically allocate memory for it on heap. so that the instance is presistent for all the functions or stacks created on your program.

    And can you tell me where and how you can design a singleton class without using dynamic allcation.
     
  7. jayaraj_ev

    jayaraj_ev New Member

    Joined:
    Aug 29, 2007
    Messages:
    20
    Likes Received:
    2
    Trophy Points:
    0
    Occupation:
    Software engineer
    Location:
    Bangalore


    Hi,
    You forgaot to add operator overloading for =() to add the counts and subtract the counts when they are assigned to NULL
     
  8. zamjad

    zamjad New Member

    Joined:
    Oct 7, 2008
    Messages:
    15
    Likes Received:
    1
    Trophy Points:
    0

    Without going into advantages and disadvantages here is the simplest implementation of Singleton without using Heap

    Code:
      Singleton* Singleton::CreateObject() 
      {
        static Singleton obj;
        return &obj;
      }
    
    This will be useful in single threaded application.
     
    shabbir likes this.
  9. jayaraj_ev

    jayaraj_ev New Member

    Joined:
    Aug 29, 2007
    Messages:
    20
    Likes Received:
    2
    Trophy Points:
    0
    Occupation:
    Software engineer
    Location:
    Bangalore
    Hi,
    Good that u tried to do one without dynamic allocation. But one thing we should be carefull about is Static objects get created at start of program, so the objects gets created before main() which calls its constructor before. How we implement inside constructor also matters.
     
  10. zamjad

    zamjad New Member

    Joined:
    Oct 7, 2008
    Messages:
    15
    Likes Received:
    1
    Trophy Points:
    0
    That happens with only global static object. Here object will create only when you call this function first time.

    In fact you can always make a program to test it. Here is the simple test program to verify this

    Code:
    #include <iostream>
    
    using std::cout;
    using std::endl;
    
    class Singleton
    {
    private:
    	Singleton()
    	{
    		cout << "Singleton::Singleton" << endl;
    	}
    
    	~Singleton() { }
    
    public:
    	static Singleton* CreateObject()
    	{
    		static Singleton obj;
    		return &obj;
    	}
    };
    
    int main()
    {
    	return 0;
    }
    
     
  11. madlex

    madlex New Member

    Joined:
    Jun 6, 2008
    Messages:
    12
    Likes Received:
    3
    Trophy Points:
    0
    Occupation:
    Technical Project Manager
    Location:
    Bucharest,RO
    Code:
    class csection{
    private:CRITICAL_SECTION sect;
    public: csection() { ::InitializeCriticalSection(&sect);} 
    ~csection() { ::DeleteCriticalSection(&sect); } 
    void lock(){::EnterCriticalSection(&sect);}
    void unlock(){::LeaveCriticalSection(&sect);}
    };
    
    csection g_singleton_cs;
    
    class singleton
    {private:
    //[...]
    singleton & get_instance()
    {
        g_singleton_cs.lock();
           static singleton instance;
        g_singleton_cs.unlock();
         return instance;
    }
    };
    
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice