A global object allows you easy access but does not prevent instantiating multiple objects. The singleton provides you with elegance of managing a single object while maintaining easy access. The instance is an ordinary object of a class but keeps the user from instantiating another object of the class. Doing this ensures that only one instance is ever created. Providing global access is done by hiding the operation that performs the instantiating behind a static method. Remember that a static method is in the class scope.
To prevent the user from being able to instantiate objects of a class, we simply disallow access to the default constructors:
protected: Singleton(); Singleton(const Singleton&); Singleton& operator= (const Singleton&);
The code prevents outside access to the constructors. The only way to use them is from within the class. It keeps the user from instantiating any objects. The next thing we will do is define a static member that points to the instantiated object of the singleton:
Singleton* m_instance;
Next, we will implement a method that will either return the instance or instantiate an object (depending on whether instantiation has already occurred or not).
Singleton* Singleton::Instance()
{
// If an instance wasn't created yet.
if (m_instance == 0)
m_instance = new Singleton;
// An instance was created so return the reference
else
return m_instance;
}
Users can only access the instance through the Instance() method. Any attempt to create an instance without this function will result to an error (since our constructors are protected). This design is quite solid, notice how the following return a pointer to the same instance:
Singleton* ptr1 = Singleton::Instance(); Singleton* ptr2 = ptr1->Instance(); Singleton& reference = *Singleton::Instance();
Preventing Memory Leaks in Single-threaded Programs
Our singleton implementation uses the new operator to dynamically allocate its instance. Since the new operator is thread-safe our design can be used in multi-threaded applications. However, our instance must be manually destroyed by calling the delete operator, otherwise we'll be introducing a memory leak. We may even get undefined behavior because our destructor will never be called. To remedy this in our single-threaded applications we can use a locally static instance instead of a dynamically allocated instance.
We can do this by modifying our previous Instance() method:
Singleton* Singleton::Instance()
{
static Singleton m_instance;
return &m_instance;
}
Here's what our final class may look like:
class Singleton
{
public:
Singleton* Instance();
protected:
Singleton();
Singleton(const Singleton&);
Singleton& operator= (const Singleton&);
};
Singleton* Singleton::Instance()
{
static Singleton m_instance;
return &m_instance;
}






MultiQuote



|