Jump to content

Let's Talk About... Singleton: Difference between revisions

From EDM2
No edit summary
Ak120 (talk | contribs)
mNo edit summary
 
(9 intermediate revisions by 2 users not shown)
Line 1: Line 1:
Written by [[Stefan Ruck]]
''Written by [[Stefan Ruck]]''


When I read about the Singleton design pattern (see [1]) for the first time,
When I read about the Singleton design pattern (see [1]) for the first time, I was really enthusiastic about it. But when I started to use it, some doubts occurred to me. These are the results of my reflections.
I was really enthusiastic about it. But when I started to use it, some
doubts occurred to me. These are the results of my reflections.


<h2>What's A Singleton?</h2>
==What's A Singleton?==
The main intention of a Singleton class is to make sure to have only one instance of this class with a global point of access to it, thus avoiding the use of global variables.


<p>The main intention of a Singleton class is to make sure to have only one
Gamma et al gave a printer spooler as an example. There may be many printers defined in an operating system, but there should only be one printer spooler.
instance of this class with a global point of access to it, thus avoiding
the use of global variables.


<p>Gamma et al gave a printer spooler as an example. There may be many
I will use the Singleton design pattern in conjunction with initialization files (see [2]). In any program you need just one instance of your initialization file. So why not design the initialization file class as a Singleton. Therefore I will expand the sample program of [2] later on.
printers defined in a operating system, but there should only be one printer
spooler.
 
<p>I will use the Singleton design pattern in conjunction with initialization
files (see [2]). In any program you need just one instance of your
initialization file. So why not design the initialization file class as a
Singleton. Therefore I will expand the sample program of [2] later on.


The declaration of a typical Singleton class is as follows:
The declaration of a typical Singleton class is as follows:
 
<pre>
<pre><small>
   class Singleton
   class Singleton
   {
   {
Line 34: Line 23:
       static Singleton * m_pInstance;
       static Singleton * m_pInstance;
   };
   };
</small></pre>
</pre>
 
''Figure 1: Declaration of a Singleton class''
<font SIZE=2>
Figure 1: Declaration of a Singleton class,
</font>
 
<p>Its implementation looks like this:


<pre><small>
Its implementation looks like this:
<pre>
   Singleton * Singleton :: m_pInstance = 0L;
   Singleton * Singleton :: m_pInstance = 0L;


Line 51: Line 36:
         : (m_pInstance = new Singleton)));
         : (m_pInstance = new Singleton)));
   }
   }
</pre>
''Figure 2: Implementation of a Singleton class''


</small></pre>
Because the constructor is not of interest, it's not shown here.
 
<font SIZE=2>
Figure 2: Implementation of a Singleton class,
</font>
 
<p>Because the constructor is not of interest, it's not shown here.
 
<p>Some explanation to those of you who aren't familiar with the ? operator.
In spoken words the code in Singleton::instance is:
 
<p>If m_pInstance is not null, return m_pInstance, else assign the address of
the new allocated Singleton object to m_pInstance and return m_pInstance.
 
<p>Like it or not, it works fine (and I like it a lot).
 
<h2>What Is The Problem?</h2>


<p>Maybe you will see there is no way to delete the Singleton object created
Some explanation to those of you who aren't familiar with the ? operator. In spoken words the code in Singleton::instance is:
during the first call of Singleton::instance.


<p>When you use a Singleton class e.g. in a Microsoft Visual C++ 4.x
If m_pInstance is not null, return m_pInstance, else assign the address of the new allocated Singleton object to m_pInstance and return m_pInstance.
environment in debug mode, you will get a warning about memory leaks when you
close the debugger. That's correct because there is no corresponding delete
to the new in Singleton::instance.


<p>OK, you can say: Why bother, as soon as the program terminates, all
Like it or not, it works fine (and I like it a lot).
allocated memory will be released.


<p>I think it's not a good programming style when there is no delete to a
==What Is The Problem?==
new. So using Singleton in the original manner, it seems to be incomplete to
Maybe you will see there is no way to delete the Singleton object created during the first call of Singleton::instance.
me.


When you use a Singleton class e.g. in a Microsoft Visual C++ 4.x environment in debug mode, you will get a warning about memory leaks when you close the debugger. That's correct because there is no corresponding delete to the new in Singleton::instance.


<p>So what can we do?
OK, you can say: Why bother, as soon as the program terminates, all allocated memory will be released.


<h2>Three Steps To Heaven</h2>
I think it's not a good programming style when there is no delete to a new. So using Singleton in the original manner, it seems to be incomplete to me.


<h3>1st Step: Singleton::destroyInstance</h3>
So what can we do?


<p>The implementation of Singleton::destroyInstance is quite easy.
==Three Steps To Heaven==
 
===1st Step: Singleton::destroyInstance===
<pre><small>
The implementation of Singleton::destroyInstance is quite easy.
<pre>
   public:
   public:
     static void destroyInstance ();
     static void destroyInstance ();
Line 105: Line 72:
     m_pInstance = 0L;
     m_pInstance = 0L;
   }
   }
</small></pre>
</pre>
''Figure 3: Declaration and implementation of Singleton::destroyInstance''


<font SIZE=2>
The question is: When do we call Singleton::destroyInstance? The safest place to call it is inside of the destructor of the main application class. This works fine as long as you remember not to use it somewhere else and none of your colleagues is going to use this class. But as soon as you call it somewhere else, you can't be sure you won't destroy the instance that's used by another class which is storing the pointer returned by Singleton::instance in a class member.
Figure 3: Declaration and implementation of Singleton::destroyInstance,
</font>


<p>The question is: When do we call Singleton::destroyInstance? The safest
So I think this isn't a safe way to destroy the allocated instance.
place to call it is inside of the destructor of the main application class.
This works fine as long as you remember not to use it somewhere else and none
of your colleagues is going to use this class. But as soon as you call it
somewhere else, you can't be sure you won't destroy the instance that's used
by another class which is storing the pointer returned by
Singleton::instance in a class member.


<p>So I think this isn't a safe way to destroy the allocated instance.
===2nd Step: Reference Counting===
What we can do is to increment a counter any time Singleton::instance is called and decrement it when Singleton::destroyInstance is called. Only when the counter is zero, will the instance be deleted. This makes sure the Singleton object is destroyed only when it is not used any more. And because destroyInstance isn't the right name any more, I will call it releaseInstance from now on.


<h3>2nd Step: Reference Counting</h3>
This leads to the following changes:
 
<pre>
<p>What we can do is to increment a counter any time Singleton::instance is
called and decrement it when Singleton::destroyInstance is called. Only when
the counter is zero, will the instance be deleted. This makes sure the
Singleton object is destroyed only when it is not used any more. And because
destroyInstance isn't the right name anymore, I will call it releaseInstance
from now on.
 
<p>This leads to the following changes:
 
<pre><small>
   public:
   public:
     static void releaseInstance ();
     static void releaseInstance ();
Line 151: Line 102:
     return (m_pInstance);
     return (m_pInstance);
   }
   }
</small></pre>
</pre>
 
''Figure 4a: Changes in Singleton for reference counting''
<font SIZE=2>
Figure 4a: Changes in Singleton for reference counting,
</font>
 
<p>Singleton::instance increases Singleton::m_ulReferenceCounter only when the
new operator is passed. It may be possible that an exception is thrown by the
constructor of Singleton. In this case the counter should not be incremented.


 
Singleton::instance increases Singleton::m_ulReferenceCounter only when the new operator is passed. It may be possible that an exception is thrown by the constructor of Singleton. In this case the counter should not be incremented.
<pre><small>
<pre>
   void Singleton :: releaseInstance ()
   void Singleton :: releaseInstance ()
   {
   {
Line 177: Line 121:
     m_pInstance = 0L;
     m_pInstance = 0L;
   }
   }
</small></pre>
</pre>
''Figure 4b: Changes in Singleton for reference counting''


<font SIZE=2>
This is much better now. Anyone who uses the Singleton instance can be sure to have a valid pointer to it. Really? Can you always be sure? What happens when Singleton::releaseInstance is called two times by the same user? Right, Singleton::m_ulReferenceCounter is decreased and maybe this time, maybe sometime later, the instance is destroyed although it is in use.
Figure 4b: Changes in Singleton for reference counting,
</font>


<p>This is much better now. Anyone who uses the Singleton instance can be
Or maybe one user 'forgets' to call Singleton::releaseInstance. So the object will stay alive until the end of the days (here: the program).
sure to have a valid pointer to it. Really? Can you always be sure? What
happens when Singleton::releaseInstance is called two times by the same user?
Right, Singleton::m_ulReferenceCounter is decreased and maybe this time,
maybe sometime later, the instance is destroyed although it is in use.


<p>Or maybe one user 'forgets' to call Singleton::releaseInstance. So the
These thoughts lead me to the final step.
object will stay alive until the end of the days (here: the program).


<p>These thoughts lead me to the final step.
===3rd Step: A Singleton Smart Template Class===
The goal is to be sure to have a legal pointer to the instance of a Singleton.


<h3>3rd Step: A Singleton Smart Template Class</h3>
I thought about it a while and came to the conclusion that as long as you deal with the Singleton class itself, you can't protect the class's users from faulty usage. My solution is a helper template class which I will call a Singleton Smart Template, in short term SingletonST. It's designed almost like smart pointers described by Scott Meyers (see [3]).


<p>The goal is to be sure to have a legal pointer to the instance of a
The SingletonST class is the only class which is able to call Singleton::instance and Singleton::releaseInstance. Together with the reference counting described in step 2, it is a safe way to use Singleton. Any Singleton interface calls are done using the SingletonST object.
Singleton.


<p>I thought about it a while and came to the conclusion that as long as you
One really important thing to maintain the goal is to define a copy constructor for the SingletonST and the Singleton class. By default, it will be defined as memberwise assignment and memberwise initialization of the class (see [4]). This seems to be OK at first. But looking at it a little bit closer, you will see it is not.
deal with the Singleton class itself, you can't protect the class's users
<pre>
from faulty usage. My solution is a helper template class which I will call
   template <class T> class SingletonST
a Singleton Smart Template, in short term SingletonST. It's designed almost
like smart pointers described by Scott Meyers (see [3]).
 
 
<p>The SingletonST class is the only class which is able to call
Singleton::instance and Singleton::releaseInstance. Together with the
reference counting described in step 2, it is a safe way to use Singleton.
Any Singleton interface calls are done using the SingletonST object.
 
<p>One really important thing to maintain the goal is to define a copy
constructor for the SingletonST and the Singleton class. By default, it
will be defined as memberwise assignment and memberwise initialization of
the class (see [4]). This seems to be OK at first. But looking at it a little
bit closer, you will see it is not.
 
<pre><small>
   template &lt;class T&gt; class SingletonST
   {
   {
     public:
     public:
Line 224: Line 145:
       ~SingletonST ();
       ~SingletonST ();


       SingletonST &lt;T&gt; &amp; operator = (const SingletonST <T> &amp; rSource);
       SingletonST <T> & operator = (const SingletonST & rSource);


       T * operator -&gt; () const;
       T * operator -> () const;


       T&amp; operator * () const;
       T&  operator * () const;


     private:
     private:
Line 234: Line 155:
   };
   };


   template &lt;class T&gt;
   template <class T>
 
   SingletonST <T> :: SingletonST ()
   SingletonST &lt;T&gt; :: SingletonST ()
         : m_pInstance (T :: instance ())
         : m_pInstance (T :: instance ())
   {
   {
   }
   }


   template &lt;class T&gt;
   template <class T>
   SingletonST &lt;T&gt; :: ~SingletonST ()
   SingletonST <T> :: ~SingletonST ()


   {
   {
     m_pInstance -&gt; releaseInstance ();
     m_pInstance -> releaseInstance ();
   }
   }


   template &lt;class T&gt;
   template <class T>
   T * ASingletonST &lt;T&gt; :: operator -&gt; () const
   T * ASingletonST <T> :: operator -> () const


   {
   {
Line 255: Line 175:
   }
   }


   template &lt;class T&gt;
   template <class T>
 
   T& ASingletonST <T> :: operator * () const
   T& ASingletonST &lt;T&gt; :: operator * () const


   {
   {
Line 266: Line 185:


   {
   {
     SingletonST &lt;Singlton&gt; * A = new SingletonST &lt;Singlton&gt;;
     SingletonST <Singlton> * A = new SingletonST <Singlton>;


     SingletonST B (*A);
     SingletonST B (*A);
Line 274: Line 193:
     delete A;
     delete A;
   }
   }
</small></pre>
</pre>
 
''Figure 5: Default copy constructor sample''
<font SIZE=2>
Figure 5: Default copy constructor sample,
</font>
 
<h2>What happens during the execution of function f()?</h2>
 
<p>When the SingletonST A is created, Singleton::instance is called by
SingletonST's constructor, and Singleton::m_ulReferenceCounter is increased.
At the creation of SingletonST B B::m_pInstance is assigned to the value of
A::m_pInstance. Singleton::instance is not called and therefore
Singleton::m_ulReferenceCounter is not increased. And we have also the
ability to create the Singlton object C directly by using the copy
constructor. The value of Singleton::m_ulReferenceCounter remains one
although we have two references to Singleton::m_pInstance (A::m_pInstance,
B::m_pInstance). And we do have more than one instance of Singleton
(Singleton::m_pInstance, C).


<p>When A is deleted, Singleton::releaseInstance is called. Because
==What happens during the execution of function f()?==
Singleton::m_ulReferenceCounter is 1, the instance of Singleton will be
When the SingletonST A is created, Singleton::instance is called by SingletonST's constructor, and Singleton::m_ulReferenceCounter is increased. At the creation of SingletonST B B::m_pInstance is assigned to the value of A::m_pInstance. Singleton::instance is not called and therefore Singleton::m_ulReferenceCounter is not increased. And we have also the ability to create the Singlton object C directly by using the copy constructor. The value of Singleton::m_ulReferenceCounter remains one although we have two references to Singleton::m_pInstance (A::m_pInstance, B::m_pInstance). And we do have more than one instance of Singleton (Singleton::m_pInstance, C).
deleted too. But B::m_pInstance still points to the no longer existing
Singleton object As soon as you try to use B to access the Singleton object
now, the program will crash. [Maybe not on the first access.  Maybe only a
little bit later after the memory you deleted has been reallocated.  These
bugs are really hard to find, so it is wise to do everything you can to
avoid them. Ed]


<p>Now the sample shows us that neither SingletonST nor Singleton should use
When A is deleted, Singleton::releaseInstance is called. Because Singleton::m_ulReferenceCounter is 1, the instance of Singleton will be deleted too. But B::m_pInstance still points to the no longer existing Singleton object As soon as you try to use B to access the Singleton object now, the program will crash. [Maybe not on the first access. Maybe only a little bit later after the memory you deleted has been reallocated. These bugs are really hard to find, so it is wise to do everything you can to avoid them. Ed]
the default copy constructor. Using SingletonST's default copy constructor
leads to wrong reference count. Singleton's default copy constructor makes
the Singleton class itself worthless. Remember, we use Singleton to have just
one and only one instance of this class.


<p>To avoid this scenario, we have to define a copy constructor for both
Now the sample shows us that neither SingletonST nor Singleton should use the default copy constructor. Using SingletonST's default copy constructor leads to wrong reference count. Singleton's default copy constructor makes the Singleton class itself worthless. Remember, we use Singleton to have just one and only one instance of this class.
classes ourselves. And because there should not be any Singleton object
created directly, the copy constructor of Singleton is declared as private.


<p>Don't worry about the dereferencing * operator in this sample. This means
To avoid this scenario, we have to define a copy constructor for both classes ourselves. And because there should not be any Singleton object created directly, the copy constructor of Singleton is declared as private.
a reference of Singleton is returned by B. You can also use A here by typing
Singleton C (**A);, which first returns the value of A (which itself is a
pointer), here a SingletonST object, and this SingletonST object returns a
reference to a Singleton object then.


<p>I do define an empty assignment operator for both classes too. Since
Don't worry about the dereferencing * operator in this sample. This means a reference of Singleton is returned by B. You can also use A here by typing Singleton C (**A);, which first returns the value of A (which itself is a pointer), here a SingletonST object, and this SingletonST object returns a reference to a Singleton object then.
SingletonST::m_pInstance is the only data member (which is initialized at
creation time of SingletonST) and has to point to the same Singleton object
over all SingletonST instances, there is no need to assign anything. And
because there is only one instance of Singleton, an assignment operator is
not needed for this class. So Singleton's assignment operator is declared a
private to prevent access to it.


<p>This is the result:
I do define an empty assignment operator for both classes too. Since SingletonST::m_pInstance is the only data member (which is initialized at creation time of SingletonST) and has to point to the same Singleton object over all SingletonST instances, there is no need to assign anything. And because there is only one instance of Singleton, an assignment operator is not needed for this class. So Singleton's assignment operator is declared a private to prevent access to it.


<pre><small>
This is the result:
   template &lt;class T&gt; class SingletonST : public IBase
<pre>
   template <class T> class SingletonST : public IBase
   {
   {
     public:
     public:
       SingletonST ();
       SingletonST ();
       SingletonST (const SingletonST &lt;T&gt; &amp; rSource);
       SingletonST (const SingletonST <T> & rSource);
       virtual ~SingletonST ();
       virtual ~SingletonST ();


       SingletonST &lt;T&gt; &amp; operator = (const SingletonST &lt;T&gt; &amp;
       SingletonST <T> & operator = (const SingletonST <T> &
 
             rSource);
             rSource);


       T * operator -&gt; () const;
       T * operator -> () const;


       T&amp; operator * () const;
       T&  operator * () const;


     private:
     private:
Line 354: Line 235:
   }
   }


   SingletonST :: SingletonST (const SingletonST&amp; rSource)
   SingletonST :: SingletonST (const SingletonST& rSource)
     : m_pInstance (Singleton :: instance ())
     : m_pInstance (Singleton :: instance ())


Line 363: Line 244:


   {
   {
     m_pInstance -&gt; releaseInstance ();
     m_pInstance -> releaseInstance ();
   }
   }


   SingletonST&amp; SingletonST :: operator = (const SingletonST&amp; rSource)
   SingletonST& SingletonST :: operator = (const SingletonST& rSource)


   {
   {
Line 372: Line 253:
   }
   }


   template &lt;class T&gt;
   template <class T>
   T * SingletonST &lt;T&gt; :: operator -&gt; () const
   T * SingletonST <T> :: operator -> () const


   {
   {
Line 379: Line 260:
   }
   }


   template &lt;class T&gt;
   template <class T>
 
   T& SingletonST <T> :: operator * () const
   T&amp; SingletonST &lt;T&gt; :: operator * () const


   {
   {
Line 398: Line 278:


     private:
     private:
       Singleton (const Singleton&amp; rSource)  { }
       Singleton (const Singleton& rSource)  { }


       Singleton&amp; operator = (const Singleton&amp; rSource) { }
       Singleton& operator = (const Singleton& rSource) { }


       static Singleton * instance ();
       static Singleton * instance ();
Line 408: Line 288:
       static unsigned long m_ulReferenceCounter;
       static unsigned long m_ulReferenceCounter;


       friend SingletonST &lt;Singleton&gt; :: SingletonST ();
       friend SingletonST <Singleton> :: SingletonST ();
       friend SingletonST &lt;Singleton&gt; :: SingletonST
       friend SingletonST <Singleton> :: SingletonST
                 (const SingletonST&amp;);
                 (const SingletonST&);
       friend SingletonST &lt;Singleton&gt; :: ~SingletonST ();
       friend SingletonST <Singleton> :: ~SingletonST ();
   };
   };
</pre>
''Figure 6: SingletonST class and final declaration of Singleton''


</small></pre>
The methods Singleton::instance and Singleton::releaseInstance stay unchanged from the 2nd step. Singleton::Method is defined for demonstration purpose only.
 
<font SIZE=2>
Figure 6: SingletonST class and final declaration of Singleton,
</font>
 
<p>The methods Singleton::instance and Singleton::releaseInstance stay
unchanged from the 2nd step. Singleton::Method is defined for demonstration
purpose only.
 
<p>The only way to access Singleton's instance now is using a SingletonST
object. Any time a SingletonST object is created, Singleton::instance is
called. This makes sure an instance of Singleton is present when needed. Any
time a SingletonST object runs out of scope, Singleton::releaseInstance is
called. When the last SingletonST object is about to be destroyed, the
Singleton object will also be. So the disadvantage seen after the 2nd step
is eliminated.
 
<p>Because the methods to create and release the Singleton object are only
accessible by the constructors / destructor of SingletonST, they cannot be
called when they should not be called; this is especially important for
Singleton::releaseInstance.
 
<p>Singleton's copy constructor and assignment operator are defined private
to disable the access to them.


<p>The copy constructor of SingletonST is equal to the default constructor.
The only way to access Singleton's instance now is using a SingletonST object. Any time a SingletonST object is created, Singleton::instance is called. This makes sure an instance of Singleton is present when needed. Any time a SingletonST object runs out of scope, Singleton::releaseInstance is called. When the last SingletonST object is about to be destroyed, the Singleton object will also be. So the disadvantage seen after the 2nd step is eliminated.
Since the only data member of SingletonST is m_pInstance, which must not be
copied to keep the reference counter of Singleton up to date, nothing needs
to be copied here. Instead Singleton::instance is called to initialize
SingletonST::m_pInstance and to be sure Singleton::m_ulReferenceCounter has
the right value.


<p>What I'm not looking at here is the case SingletonST is created by the new
Because the methods to create and release the Singleton object are only accessible by the constructors / destructor of SingletonST, they cannot be called when they should not be called; this is especially important for Singleton::releaseInstance.
operator and not deleted. I think there is always a way to bypass the
security built into classes.


<h2>The Advantages</h2>
Singleton's copy constructor and assignment operator are defined private to disable the access to them.


<p>I think the advantages of this solution are clear. The users of SingletonST
The copy constructor of SingletonST is equal to the default constructor. Since the only data member of SingletonST is m_pInstance, which must not be copied to keep the reference counter of Singleton up to date, nothing needs to be copied here. Instead Singleton::instance is called to initialize SingletonST::m_pInstance and to be sure Singleton::m_ulReferenceCounter has the right value.
can always be sure to have a valid access to Singleton. And they can always
be sure that any memory allocated during run time is cleaned up.


<h2>The Disadvantages</h2>
What I'm not looking at here is the case SingletonST is created by the new operator and not deleted. I think there is always a way to bypass the security built into classes.


<p>Since the SingletonST has to be defined only once for all singleton you'll
==The Advantages==
ever use, I can't see any disadvantages.
I think the advantages of this solution are clear. The users of SingletonST can always be sure to have a valid access to Singleton. And they can always be sure that any memory allocated during run time is cleaned up.


<p>If you need implicit type conversion and use CSet++ or VAC++, you need to
==The Disadvantages==
do some work, because none of them supports member function templates, as far
Since the SingletonST has to be defined only once for all singleton you'll ever use, I can't see any disadvantages.
as I've tested it.


<p>When your compiler supports it, you just have to add the following member
If you need implicit type conversion and use C Set++ or VAC++, you need to do some work, because none of them supports member function templates, as far as I've tested it.
function to SingletonST


<pre><small>
When your compiler supports it, you just have to add the following member function to SingletonST
   template &lt;class newType&gt;
<pre>
   operator SingletonST &lt;newType&gt;
   template <class newType>
   operator SingletonST <newType>
     {
     {
     return (SingletonST &lt;newType&gt; (m_pInstance));
     return (SingletonST <newType> (m_pInstance));
     }
     }
</pre>
''Figure 7: Member function template for implicit type conversion''


</small></pre>
All this stuff is perfectly discussed by Scott Meyers (see [5]).


<font SIZE=2>
==The Sample Application==
Figure 7: Member function template for implicit type conversion,
AIniConfig is defined in inicfg.hpp and inicfg.cpp, ASingletonST is defined in singlest.h and singlest.c.
</font>


<p>All this stuff is perfectly discussed by Scott Meyers (see [5]).
For the sample program of this article I decided to use the source code of my INI file article (see [2]) as base. The original program saves its size and position into INI files. Here, I'm not using the AConfigFile and ACfgConfig classes, although ACfgConfig could be designed as a singleton too.


<h2>The Sample Application</h2>
I've added the ability to save the size to AIniConfig and re-designed it as a singleton class.


<p>AIniConfig is defined in inicfg.hpp and inicfg.cpp, ASingletonST is defined
This is what it looks like now:
in singlest.h and singlest.c.
<pre>
 
<p>For the sample program of this article I decided to use the source code of
my INI file article (see [2]) as base. The original program saves its size
and position into INI files. Here, I'm not using the AConfigFile and
ACfgConfig classes, although ACfgConfig could be designed as a singleton too.
 
<p>I've added the ability to save the size to AIniConfig and re-designed it
as a singleton class.
 
<p>This is what it looks like now:
 
<pre><small>
   class AIniConfig : public AIniFile
   class AIniConfig : public AIniFile
   {
   {
Line 518: Line 355:
     // retrieve the size
     // retrieve the size


       AIniConfig&amp; setSize (const SIZEL sSize);
       AIniConfig& setSize (const SIZEL sSize);
     // save the size
     // save the size


Line 525: Line 362:


     private:
     private:
       AIniConfig (const AIniConfig&amp; rSource) { }
       AIniConfig (const AIniConfig& rSource) { }


       AIniConfig &amp; operator = (const AIniConfig&amp; rSource)
       AIniConfig & operator = (const AIniConfig& rSource)
       { return (*this); }
       { return (*this); }


Line 547: Line 384:
     // size key name
     // size key name


       friend ASingletonST &lt;AIniConfig&gt; :: ASingletonST ();
       friend ASingletonST <AIniConfig> :: ASingletonST ();
       friend ASingletonST &lt;AIniConfig&gt; :: ASingletonST
       friend ASingletonST <AIniConfig> :: ASingletonST
         (const ASingletonST &lt;AIniConfig&gt; &amp;);
         (const ASingletonST <AIniConfig> &);
       friend ASingletonST &lt;AIniConfig&gt; :: ~ASingletonST ();
       friend ASingletonST <AIniConfig> :: ~ASingletonST ();
   };
   };
</pre>
''Figure 8: Declaration of AIniConfig as a singleton class''


</small></pre>
As you can see above, it isn't AIniFile, the base class of AIniConfig, that is changed into a singleton class, but AIniConfig.


<font SIZE=2>
In my opinion it doesn't make much sense to design a base class as a singleton, although it is described by Gamma et al. I really don't like the way they suggested to decide which class is to be created when the Singleton::instance method of a base class is called (using an environment variable). As soon as the application's user deletes the setting of this variable, the whole program crashes and you will search a long time for the reason. Another point is as soon as a new derived class arises, you have to change the base class too. Passing a parameter to the base class's instance method will eliminate the environment variable problem, but not the need for changes in the base class when you create a new derived class.
Figure 8: Declaration of AIniConfig as a singleton class,
</font>


<p>As you can see above, it isn't AIniFile, the base class of AIniConfig,
The new class, ASingletonST, is the only way to access the initialization file now. It looks exactly like SingletonST described above.
that is changed into a singleton class, but AIniConfig.


<p>In my opinion it doesn't make much sense to design a base class as a
To prove that all instances of ASingletonST work with the same instance of AIniConfig, the sample has two ASingletonST objects. One reads the size and saves the position and the other reads the position and writes the size of the window. This is only for demonstration purposes. In real life, it is not very useful.
singleton, although it is described by Gamma et al. I really don't like the
way they suggested to decide which class is to be created when the
Singleton::instance method of a base class is called (using an environment
variable). As soon as the application's user deletes the setting of this
variable, the whole program crashes and you will search a long time for the
reason. Another point is as soon as a new derived class arises, you have to
change the base class too. Passing a parameter to the base class's instance
method will eliminate the environment variable problem, but not the need for
changes in the base class when you create a new derived class.


<p>The new class, ASingletonST, is the only way to access the initialization
When you run the sample, be sure to close it using the F3 key or the menu 'File' 'Exit'. Any other way to end it doesn't save the current position and size of the window.
file now. It looks exactly like SingletonST described above.


<p>To prove that all instances of ASingletonST work with the same instance of
When you build the sample application, you can use the makefiles included in the source zip. There is one makefile for IBM's C Set++ (singletn.mac, not a MAC file) and one for IBM's Visual Age for C++ (singletn.mav). If you decide to use your own makefile, make sure all switches especially for the use of templates are set correctly.
AIniConfig, the sample has two ASingletonST objects. One reads the size and
saves the position and the other reads the position and writes the size of
the window. This is only for demonstration purposes. In real life, it is not
very useful.


<p>When you run the sample, be sure to close it using the F3 key or the menu
==Wanted: Your Opinion==
'File' 'Exit'. Any other way to end it doesn't save the current position and
Now these are my very personal thoughts and conclusions on the Singleton design pattern. As said above, I do not agree with Gamma et al in all points of their design.
size of the window.


<p>When you build the sample application, you can use the makefiles included
A colleague of mine mentioned it is not worth it to build a class around the singleton. On the other hand a friend was really interested in my idea. So what do you think? Please give me your feed back on my e-mail address 100664.1353@compuserve.com. If there is an interest, I will write a summary of the discussion later on.
in the source zip. There is one makefile for IBM's CSet++ (singletn.mac, not
a MAC file) and one for IBM's Visual Age for C++ (singletn.mav). If you
decide to use your own makefile, make sure all switches especially for the
use of templates are set correctly.


<h2>Wanted: Your Opinion</h2>
==Acknowledgement==
I'd like to thank Farhad Fouladi who recommended me to buy 'Design Patterns' and drew my attention to the copy constructor and assignment operator. Many thanks also to my girlfriend Elke Ungeheuer who re-read this article like all the others although she's not a programmer (and absolutely not interested in this stuff).


<p>Now these are my very personal thoughts and conclusions on the Singleton
Last but not least thanks to the folks of EDM/2 for final editing.
design pattern. As said above, I do not agree with Gamma et al in all points
of their design.


<p>A colleague of mine mentioned it is not worth it to build a class around
[1] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides;
the singleton. On the other hand a friend was really interested in my idea.
Design Patterns, 1995, p. 127ff
So what do you think? Please give me your feed back on my e-mail address
100664.1353@compuserve.com. If there is an interest, I will write a summary
of the discussion later on.


[2] Stefan Ruck,
[[Manage Your Configuration Files and Data]], EDM/2 volume 5 issue 9, 1997


<h2>Acknowledgement</h2>
[3] Scott Meyers,
More Effective C++, 1996, p. 159ff


<p>I'd like to thank Farhad Fouladi who recommended me to buy 'Design Patterns'
[4] Bjarne Stroustrup,
and drew my attention to the copy constructor and assignment operator. Many
The C++ Programming Language, Second Edition, 1995, p. 582
thanks also to my girlfriend Elke Ungeheuer who re-read this article like
all the others although she's not a programmer (and absolutely not
interested in this stuff).


<p>Last but not least thanks to the folks of EDM/2 for final editing.
<pre><small>
[1] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides;
    Design Patterns, 1995, p. 127ff
[2] Stefan Ruck,
    Manage Your Configuration Files and Data, EDM/2 volume 5 issue 9, 1997
[3] Scott Meyers,
    More Effective C++, 1996, p. 159ff
[4] Bjarne Stroustrup,
    The C++ Programming Language, Second Edition, 1995, p. 582
[5] Scott Meyers,
[5] Scott Meyers,
    More Effective C++, 1996, p. 173ff</pre>
More Effective C++, 1996, p. 173ff


[[Category:Languages Articles]]
[[Category:C++ Articles]]

Latest revision as of 18:00, 23 March 2018

Written by Stefan Ruck

When I read about the Singleton design pattern (see [1]) for the first time, I was really enthusiastic about it. But when I started to use it, some doubts occurred to me. These are the results of my reflections.

What's A Singleton?

The main intention of a Singleton class is to make sure to have only one instance of this class with a global point of access to it, thus avoiding the use of global variables.

Gamma et al gave a printer spooler as an example. There may be many printers defined in an operating system, but there should only be one printer spooler.

I will use the Singleton design pattern in conjunction with initialization files (see [2]). In any program you need just one instance of your initialization file. So why not design the initialization file class as a Singleton. Therefore I will expand the sample program of [2] later on.

The declaration of a typical Singleton class is as follows:

  class Singleton
  {
    public:
      static Singleton * instance ();

    protected:
      Singleton ();

    private:
      static Singleton * m_pInstance;
  };

Figure 1: Declaration of a Singleton class

Its implementation looks like this:

  Singleton * Singleton :: m_pInstance = 0L;

  Singleton * Singleton :: instance ()

  {
    return ((m_pInstance ? m_pInstance
        : (m_pInstance = new Singleton)));
  }

Figure 2: Implementation of a Singleton class

Because the constructor is not of interest, it's not shown here.

Some explanation to those of you who aren't familiar with the ? operator. In spoken words the code in Singleton::instance is:

If m_pInstance is not null, return m_pInstance, else assign the address of the new allocated Singleton object to m_pInstance and return m_pInstance.

Like it or not, it works fine (and I like it a lot).

What Is The Problem?

Maybe you will see there is no way to delete the Singleton object created during the first call of Singleton::instance.

When you use a Singleton class e.g. in a Microsoft Visual C++ 4.x environment in debug mode, you will get a warning about memory leaks when you close the debugger. That's correct because there is no corresponding delete to the new in Singleton::instance.

OK, you can say: Why bother, as soon as the program terminates, all allocated memory will be released.

I think it's not a good programming style when there is no delete to a new. So using Singleton in the original manner, it seems to be incomplete to me.

So what can we do?

Three Steps To Heaven

1st Step: Singleton::destroyInstance

The implementation of Singleton::destroyInstance is quite easy.

  public:
    static void destroyInstance ();

  void Singleton :: destroyInstance ()

  {
    delete m_pInstance;

    m_pInstance = 0L;
  }

Figure 3: Declaration and implementation of Singleton::destroyInstance

The question is: When do we call Singleton::destroyInstance? The safest place to call it is inside of the destructor of the main application class. This works fine as long as you remember not to use it somewhere else and none of your colleagues is going to use this class. But as soon as you call it somewhere else, you can't be sure you won't destroy the instance that's used by another class which is storing the pointer returned by Singleton::instance in a class member.

So I think this isn't a safe way to destroy the allocated instance.

2nd Step: Reference Counting

What we can do is to increment a counter any time Singleton::instance is called and decrement it when Singleton::destroyInstance is called. Only when the counter is zero, will the instance be deleted. This makes sure the Singleton object is destroyed only when it is not used any more. And because destroyInstance isn't the right name any more, I will call it releaseInstance from now on.

This leads to the following changes:

  public:
    static void releaseInstance ();

  private:
    static unsigned long m_ulReferenceCounter;

  unsigned long Singleton :: m_ulReferenceCounter = 0L;

  Singleton * Singleton :: instance ()

  {
    if (!m_pInstance)
        m_pInstance = new Singleton;

    ++m_ulReferenceCounter;

    return (m_pInstance);
  }

Figure 4a: Changes in Singleton for reference counting

Singleton::instance increases Singleton::m_ulReferenceCounter only when the new operator is passed. It may be possible that an exception is thrown by the constructor of Singleton. In this case the counter should not be incremented.

  void Singleton :: releaseInstance ()
  {
    if (!m_ulReferenceCounter) // no instance?
        return;                // yes, return

    --m_ulReferenceCounter;

    if (m_ulReferenceCounter) // is the instance still in use?
        return;               // yes, don't destroy it

    delete m_pInstance;

    m_pInstance = 0L;
  }

Figure 4b: Changes in Singleton for reference counting

This is much better now. Anyone who uses the Singleton instance can be sure to have a valid pointer to it. Really? Can you always be sure? What happens when Singleton::releaseInstance is called two times by the same user? Right, Singleton::m_ulReferenceCounter is decreased and maybe this time, maybe sometime later, the instance is destroyed although it is in use.

Or maybe one user 'forgets' to call Singleton::releaseInstance. So the object will stay alive until the end of the days (here: the program).

These thoughts lead me to the final step.

3rd Step: A Singleton Smart Template Class

The goal is to be sure to have a legal pointer to the instance of a Singleton.

I thought about it a while and came to the conclusion that as long as you deal with the Singleton class itself, you can't protect the class's users from faulty usage. My solution is a helper template class which I will call a Singleton Smart Template, in short term SingletonST. It's designed almost like smart pointers described by Scott Meyers (see [3]).

The SingletonST class is the only class which is able to call Singleton::instance and Singleton::releaseInstance. Together with the reference counting described in step 2, it is a safe way to use Singleton. Any Singleton interface calls are done using the SingletonST object.

One really important thing to maintain the goal is to define a copy constructor for the SingletonST and the Singleton class. By default, it will be defined as memberwise assignment and memberwise initialization of the class (see [4]). This seems to be OK at first. But looking at it a little bit closer, you will see it is not.

  template <class T> class SingletonST
  {
    public:
      SingletonST ();
      ~SingletonST ();

      SingletonST <T> & operator = (const SingletonST  & rSource);

      T * operator -> () const;

      T&  operator * () const;

    private:
      T * m_pInstance;
  };

  template <class T>
  SingletonST <T> :: SingletonST ()
        : m_pInstance (T :: instance ())
  {
  }

  template <class T>
  SingletonST <T> :: ~SingletonST ()

  {
    m_pInstance -> releaseInstance ();
  }

  template <class T>
  T * ASingletonST <T> :: operator -> () const

  {
    return (m_pInstance);
  }

  template <class T>
  T& ASingletonST <T> :: operator * () const

  {
    return (*m_pInstance);
  }

  void f ()

  {
    SingletonST <Singlton> * A = new SingletonST <Singlton>;

    SingletonST B (*A);

    Singleton C (*B);

    delete A;
  }

Figure 5: Default copy constructor sample

What happens during the execution of function f()?

When the SingletonST A is created, Singleton::instance is called by SingletonST's constructor, and Singleton::m_ulReferenceCounter is increased. At the creation of SingletonST B B::m_pInstance is assigned to the value of A::m_pInstance. Singleton::instance is not called and therefore Singleton::m_ulReferenceCounter is not increased. And we have also the ability to create the Singlton object C directly by using the copy constructor. The value of Singleton::m_ulReferenceCounter remains one although we have two references to Singleton::m_pInstance (A::m_pInstance, B::m_pInstance). And we do have more than one instance of Singleton (Singleton::m_pInstance, C).

When A is deleted, Singleton::releaseInstance is called. Because Singleton::m_ulReferenceCounter is 1, the instance of Singleton will be deleted too. But B::m_pInstance still points to the no longer existing Singleton object As soon as you try to use B to access the Singleton object now, the program will crash. [Maybe not on the first access. Maybe only a little bit later after the memory you deleted has been reallocated. These bugs are really hard to find, so it is wise to do everything you can to avoid them. Ed]

Now the sample shows us that neither SingletonST nor Singleton should use the default copy constructor. Using SingletonST's default copy constructor leads to wrong reference count. Singleton's default copy constructor makes the Singleton class itself worthless. Remember, we use Singleton to have just one and only one instance of this class.

To avoid this scenario, we have to define a copy constructor for both classes ourselves. And because there should not be any Singleton object created directly, the copy constructor of Singleton is declared as private.

Don't worry about the dereferencing * operator in this sample. This means a reference of Singleton is returned by B. You can also use A here by typing Singleton C (**A);, which first returns the value of A (which itself is a pointer), here a SingletonST object, and this SingletonST object returns a reference to a Singleton object then.

I do define an empty assignment operator for both classes too. Since SingletonST::m_pInstance is the only data member (which is initialized at creation time of SingletonST) and has to point to the same Singleton object over all SingletonST instances, there is no need to assign anything. And because there is only one instance of Singleton, an assignment operator is not needed for this class. So Singleton's assignment operator is declared a private to prevent access to it.

This is the result:

  template <class T> class SingletonST : public IBase
  {
    public:
      SingletonST ();
      SingletonST (const SingletonST <T> & rSource);
      virtual ~SingletonST ();

      SingletonST <T> & operator = (const SingletonST <T> &
            rSource);

      T * operator -> () const;

      T&  operator * () const;

    private:
      T * m_pInstance;
  };

  SingletonST :: SingletonST ()
    : m_pInstance (Singleton :: instance ())

  {
  }

  SingletonST :: SingletonST (const SingletonST& rSource)
    : m_pInstance (Singleton :: instance ())

  {
  }

  SingletonST :: ~SingletonST ()

  {
    m_pInstance -> releaseInstance ();
  }

  SingletonST& SingletonST :: operator = (const SingletonST& rSource)

  {
    return (*this);
  }

  template <class T>
  T * SingletonST <T> :: operator -> () const

  {
    return (m_pInstance);
  }

  template <class T>
  T& SingletonST <T> :: operator * () const

  {
    return (*m_pInstance);
  }


  class Singleton

  {
    public:
      void Method ();

    protected:
      Singleton ();

    private:
      Singleton (const Singleton& rSource)  { }

      Singleton& operator = (const Singleton& rSource) { }

      static Singleton * instance ();
      static void releaseInstance ();

      static Singleton * m_pInstance;
      static unsigned long m_ulReferenceCounter;

      friend SingletonST <Singleton> :: SingletonST ();
      friend SingletonST <Singleton> :: SingletonST
                (const SingletonST&);
      friend SingletonST <Singleton> :: ~SingletonST ();
  };

Figure 6: SingletonST class and final declaration of Singleton

The methods Singleton::instance and Singleton::releaseInstance stay unchanged from the 2nd step. Singleton::Method is defined for demonstration purpose only.

The only way to access Singleton's instance now is using a SingletonST object. Any time a SingletonST object is created, Singleton::instance is called. This makes sure an instance of Singleton is present when needed. Any time a SingletonST object runs out of scope, Singleton::releaseInstance is called. When the last SingletonST object is about to be destroyed, the Singleton object will also be. So the disadvantage seen after the 2nd step is eliminated.

Because the methods to create and release the Singleton object are only accessible by the constructors / destructor of SingletonST, they cannot be called when they should not be called; this is especially important for Singleton::releaseInstance.

Singleton's copy constructor and assignment operator are defined private to disable the access to them.

The copy constructor of SingletonST is equal to the default constructor. Since the only data member of SingletonST is m_pInstance, which must not be copied to keep the reference counter of Singleton up to date, nothing needs to be copied here. Instead Singleton::instance is called to initialize SingletonST::m_pInstance and to be sure Singleton::m_ulReferenceCounter has the right value.

What I'm not looking at here is the case SingletonST is created by the new operator and not deleted. I think there is always a way to bypass the security built into classes.

The Advantages

I think the advantages of this solution are clear. The users of SingletonST can always be sure to have a valid access to Singleton. And they can always be sure that any memory allocated during run time is cleaned up.

The Disadvantages

Since the SingletonST has to be defined only once for all singleton you'll ever use, I can't see any disadvantages.

If you need implicit type conversion and use C Set++ or VAC++, you need to do some work, because none of them supports member function templates, as far as I've tested it.

When your compiler supports it, you just have to add the following member function to SingletonST

  template <class newType>
  operator SingletonST <newType>
    {
    return (SingletonST <newType> (m_pInstance));
    }

Figure 7: Member function template for implicit type conversion

All this stuff is perfectly discussed by Scott Meyers (see [5]).

The Sample Application

AIniConfig is defined in inicfg.hpp and inicfg.cpp, ASingletonST is defined in singlest.h and singlest.c.

For the sample program of this article I decided to use the source code of my INI file article (see [2]) as base. The original program saves its size and position into INI files. Here, I'm not using the AConfigFile and ACfgConfig classes, although ACfgConfig could be designed as a singleton too.

I've added the ability to save the size to AIniConfig and re-designed it as a singleton class.

This is what it looks like now:

  class AIniConfig : public AIniFile
  {
    public:
      Boolean isPositionSaved () const;
    // is a value for the position in the profile?

      POINTL getPosition () const;
    // retrieve the position

      AIniConfig& setPosition (const POINTL pPosition);
    // save the position

      Boolean isSizeSaved () const;
    // is a value for the size in the profile?

      SIZEL getSize () const;
    // retrieve the size

      AIniConfig& setSize (const SIZEL sSize);
    // save the size

    protected:
      AIniConfig () { }

    private:
      AIniConfig (const AIniConfig& rSource) { }

      AIniConfig & operator = (const AIniConfig& rSource)
      { return (*this); }

      static AIniConfig * instance ();
    // return an instance

      static void releaseInstance ();
    // release an instance

      static AIniConfig * m_pInstance;
    // pointer to the single instance

      static unsigned long m_ulReferenceCounter;
    // counts the instance requests

      static const char * const m_pszPosition;
    // position key name
      static const char * const m_pszSize;
    // size key name

      friend ASingletonST <AIniConfig> :: ASingletonST ();
      friend ASingletonST <AIniConfig> :: ASingletonST
        (const ASingletonST <AIniConfig> &);
      friend ASingletonST <AIniConfig> :: ~ASingletonST ();
  };

Figure 8: Declaration of AIniConfig as a singleton class

As you can see above, it isn't AIniFile, the base class of AIniConfig, that is changed into a singleton class, but AIniConfig.

In my opinion it doesn't make much sense to design a base class as a singleton, although it is described by Gamma et al. I really don't like the way they suggested to decide which class is to be created when the Singleton::instance method of a base class is called (using an environment variable). As soon as the application's user deletes the setting of this variable, the whole program crashes and you will search a long time for the reason. Another point is as soon as a new derived class arises, you have to change the base class too. Passing a parameter to the base class's instance method will eliminate the environment variable problem, but not the need for changes in the base class when you create a new derived class.

The new class, ASingletonST, is the only way to access the initialization file now. It looks exactly like SingletonST described above.

To prove that all instances of ASingletonST work with the same instance of AIniConfig, the sample has two ASingletonST objects. One reads the size and saves the position and the other reads the position and writes the size of the window. This is only for demonstration purposes. In real life, it is not very useful.

When you run the sample, be sure to close it using the F3 key or the menu 'File' 'Exit'. Any other way to end it doesn't save the current position and size of the window.

When you build the sample application, you can use the makefiles included in the source zip. There is one makefile for IBM's C Set++ (singletn.mac, not a MAC file) and one for IBM's Visual Age for C++ (singletn.mav). If you decide to use your own makefile, make sure all switches especially for the use of templates are set correctly.

Wanted: Your Opinion

Now these are my very personal thoughts and conclusions on the Singleton design pattern. As said above, I do not agree with Gamma et al in all points of their design.

A colleague of mine mentioned it is not worth it to build a class around the singleton. On the other hand a friend was really interested in my idea. So what do you think? Please give me your feed back on my e-mail address 100664.1353@compuserve.com. If there is an interest, I will write a summary of the discussion later on.

Acknowledgement

I'd like to thank Farhad Fouladi who recommended me to buy 'Design Patterns' and drew my attention to the copy constructor and assignment operator. Many thanks also to my girlfriend Elke Ungeheuer who re-read this article like all the others although she's not a programmer (and absolutely not interested in this stuff).

Last but not least thanks to the folks of EDM/2 for final editing.

[1] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides; Design Patterns, 1995, p. 127ff

[2] Stefan Ruck, Manage Your Configuration Files and Data, EDM/2 volume 5 issue 9, 1997

[3] Scott Meyers, More Effective C++, 1996, p. 159ff

[4] Bjarne Stroustrup, The C++ Programming Language, Second Edition, 1995, p. 582

[5] Scott Meyers, More Effective C++, 1996, p. 173ff