By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
432,463 Members | 771 Online
Bytes IT Community
Submit an Article
Got Smarts?
Share your bits of IT knowledge by writing an article on Bytes.

Design Patterns: The Singleton

weaknessforcats
Expert Mod 5K+
P: 9,197
Design Pattern: The Singleton

Overview

Use the Singleton Design Pattern when you want to have only one instance of a class. This single instance must have a single global point of access. That is, regardless of where the object is hidden, everyone needs access to it.

The global point of access is the object's Instance() method.

Individual users need to be prevented from creating their own instances of the Singleton.

The Singleton represents a replacement for a global variable. Professional C++ developers avoid global variables for a variety of reasons.

Where many Singleton objects are required by an application, it may be necessary to construct a container of Singletons called a registry. Here individual Singleton objects can be stored with a key for retrieval.

A Singleton Class Part 1

The SysParms class shown represents a class capable of being a Singleton. The default constructor is protected so only a derived class can execute it. This makes it possible to create an instance of the derived class but not an instance of the SysParms class itself.

Likewise the SysParms destructor is protected so it can be called only by a derived object. This will prevent just any user from deleting the Singleton. Of course, the destructor is virtual which allows the SysParms class to be used with polymorphism.

The Instance() method is static so you do not need a SysParms object to call it. In fact, it will be the Instance() method that you will use to create the SysParms object in the first place. This is called the lazy method of creating the object.

Expand|Select|Wrap|Line Numbers
  1.  
  2. class SysParms
  3. {     
  4.    private:
  5.         SysParms(const SysParms&);  //Prevents making a copy
  6.  
  7.    protected:
  8.         SysParms();       //Only a SysParms member can call this
  9.                      //Prevents a user from creating singleton objects
  10.         virtual ~SysParms();  //Prevents just anyone from deleting the singleton
  11.  
  12.    public:
  13.  
  14.  
  15.        //The "official" access point.
  16.        static SysParms* Instance();
  17.  
  18. };
  19.  
  20.  
The implementation of the SysParms class would be in a source file by itself.

Here you see the address of the single instance implemented as a variable inside an anonymous namespace. The reason for this is that the members of an anonymous namespace are in accessible outside the file where the namespace resides. That is, they have internal linkage.

The initial value of the address of the instance is set to 0. The SysParms singleton will not actually by created until it is required.

The static SysParms::Instance() method simply checks the address of the instance in the anonymous namespace and if the address is zero, the method creates a new SysParms object on the heap and stores the heap address in the namespace.

If the address in the anonymous namespace is not zero, it means the singleton has already been created. In this case, the Instance() methods just returns the address in the namespace.

Expand|Select|Wrap|Line Numbers
  1.  
  2. //Use anonymous namespace to force internal linkage for instance
  3.  
  4. namespace
  5. {
  6.     SysParms* instance = 0;        //Address of the singleton
  7.  
  8. }
  9.  
  10. SysParms::SysParms()
  11. {
  12.  
  13. }
  14. SysParms::~SysParms()
  15. {
  16.     delete instance;
  17.                 instance = 0;
  18. }
  19.  
  20. //The "official" access point
  21. SysParms* SysParms::Instance()
  22. {
  23.    //"Lazy" initialization. Singleton not created until it's needed
  24.    if (!instance)
  25.    {
  26.       instance = new SysParms;
  27.    }
  28.    return instance;
  29. }
  30.  
Using a Simple Singleton

To use the SysParms singleton, you need only call the Instance() method and you will receive the address of the singleton.

From there, you may execute whatever methods are on the singleton.

Expand|Select|Wrap|Line Numbers
  1. int main()
  2. {
  3.    SysParms* theSysParmsSingleton;
  4.    cout << "Example A: Creating only one instance" << endl;
  5.  
  6.    //
  7.    //SysParms obj;  //ERROR: singleton objects cannot be created by the user
  8.    //
  9.    //Use Instance() method to locate singleton. 
  10.    //Instance() is static as we have no object
  11.    theSysParmsSingleton = SysParms::Instance();
  12.    //
  13.    cout << "theSysParmsSingleton is located at " << &theSysParmsSingleton 
  14.           << endl;
  15.  
  16.  
  17.  
  18.  
  19.    return 0;
  20. }
  21.  
A Singleton Class Part 2

In this second part, the SysParms class is expanded and a derived class is added. Instances of the derived class, which are all singletons, are stored in a container, called a registry, with a retrieval key.

In this example, the singleton objects deal with sort sequences so there will be a singleton object for each of the sort sequences.

In this case, the sort sequences are either ascending or descending.

A Register() method has been added to store the singleton in the registry.

A Lookup() method has been added to retrieve singletons from the registry.

Lastly, there are accessor methods for changing or retrieving parameters from the singleton. For ease, the parameters are just a simple enum. In a real program the parameters would be returned as an object of a Parameter class.

Please note: Public virtual functions are discouraged because public virtual functions violate the concept of separation of interface from implementation. They are shown as public in this example for instructional purposes only.

Expand|Select|Wrap|Line Numbers
  1. class SysParms
  2.     private:
  3.          SysParms(const SysParms&);  //Prevents making a copy
  4.  
  5.     protected:
  6.          SysParms();       //Only a SysParms member or subclass can call this
  7.          virtual ~SysParms(); //Prevent just anyone from deleting the singleton
  8.  
  9.     public:
  10.        enum Parms {ASCENDING, DESCENDING};
  11.  
  12.  
  13.  
  14.     //The "official" access point. 
  15.     static SysParms* Instance(const std::string& name);
  16.  
  17.     //Register the singleton object by a name
  18.     //Returns false if registration failed
  19.     bool Register(const std::string& name, SysParms* obj);
  20.  
  21.     //Returns 0 if name not in registry
  22.     SysParms* Lookup(const std::string& name);
  23.  
  24.    //Parameter Interface:
  25.    //These should really be private. 
  26.    virtual Parms GetParms()= 0; 
  27.    virtual void SetParms(Parms in) = 0;
  28.  
  29. }; 
  30.  
The SysParms implementation file now expands to:

Expand|Select|Wrap|Line Numbers
  1. extern SingletonRegistry theRegistry;
  2.  
  3.  
  4. SysParms::SysParms()
  5. {
  6.  
  7. }
  8. SysParms::~SysParms()
  9. {
  10.  
  11. }
  12.  
  13. //The "official" access point
  14. //
  15. //You look up the singleton in theRegistry and return its address
  16. //
  17. SysParms* SysParms::Instance(const string& name)
  18. {
  19.  
  20.     return theRegistry.Lookup(name);
  21.  
  22. }
  23.  
  24. bool SysParms::Register(const string& name, SysParms* obj)
  25. {
  26.     return theRegistry.Add(name, obj);
  27. }
  28.  
  29. SysParms* SysParms::Lookup(const string& name)
  30. {
  31.     return theRegistry.Lookup(name);
  32. }
  33.  
You can see the implementation of the Register() and Lookup() methods. The registry itself is in its own implementation file.

Here an STL map is used for the registry.

Expand|Select|Wrap|Line Numbers
  1. class SingletonRegistry
  2. {
  3.    private:
  4.     std::map<std::string, SysParms*> theRegistry;        
  5.  
  6.    public:
  7.     //Default ctor and dtor of std::map<> will be used
  8.  
  9.     //Locate 
  10.     SysParms*   Lookup(const std::string& name);        
  11.  
  12.     //Add
  13.     bool Add(const std::string& name, SysParms* obj);
  14. };
  15.  
The registry implementation is shown below.

A singleton is stored in the registry simply by creating a pair<> object with the supplied string as the retrieval key and a pointer to the singleton object. The pair is inserted in the map container.

A singleton is retrieved by accessing the map container using the string specified to retrieve the pair object from the map container. The second member of the pair is the address of the singleton corresponding to the supplied string. The method returns a null pointer if the singleton is not in the registry.

Expand|Select|Wrap|Line Numbers
  1. extern SingletonRegistry theRegistry;
  2.  
  3. //Registry of Singletons.
  4.  
  5. SysParms* SingletonRegistry::Lookup(const string& name)
  6. {
  7.     pair<string, SysParms*> theEntry;
  8.     map<string, SysParms*>::iterator itr = theRegistry.find(name);
  9.     if (itr == theRegistry.end())
  10.     {
  11.         return 0;  //not found
  12.     }
  13.     return itr->second;    
  14. }
  15.  
  16. bool SingletonRegistry::Add(const string& name, SysParms* obj)
  17. {
  18.     pair<string, SysParms*> theEntry;
  19.     theEntry.first = name;
  20.     theEntry.second = obj;
  21.  
  22.     theRegistry.insert(theEntry);
  23.  
  24.     return true;  
  25.  
  26. }
  27.  
The singletons being put in the registry are:
Expand|Select|Wrap|Line Numbers
  1. class SortParms :public SysParms
  2. {
  3.  
  4.    private:
  5.      enum  Parms seq;
  6.  
  7.  
  8.    public:
  9.     SortParms(const std::string& name);
  10.     ~SortParms();
  11.     virtual Parms GetParms(); 
  12.     virtual void SetParms(Parms in);
  13. };
  14.  
And are implemented as:

Expand|Select|Wrap|Line Numbers
  1. extern SingletonRegistry theRegistry;
  2.  
  3. SortParms::SortParms(const std::string& name)
  4. {
  5.     theRegistry.Add(name, this);
  6. }
  7. SortParms::~SortParms()
  8. {
  9.  
  10. }
  11.  
  12.  
  13.  
  14. enum SysParms::Parms SortParms::GetParms()
  15. {
  16.     return this->seq;
  17.  
  18. }
  19. void SortParms::SetParms(Parms in)
  20. {
  21.         this->seq = in;
  22. }
  23.  
Notice these singletons register themselves when they are created by having the register code as part of the singleton constructor.

In a separate source file, the registry is created followed by the singletons. Again, the singleton objects are in an anonymous namespace which makes them inaccessible from anywhere. The only global point of reference not is the Lookup() method on the registry. Here is the implementation:

Expand|Select|Wrap|Line Numbers
  1. //The Registry object
  2.  
  3.     SingletonRegistry theRegistry;
  4. //
  5. //The SortParms objects
  6. //
  7. //The anonymous namespace makes them publicly inaccessible outside this file.
  8. //
  9. namespace
  10. {
  11.    SortParms obj(std::string("SortParms"));       //The singleton itself
  12.    SortParms obj1(std::string("SortParmsForPerson"));    //The singleton itself
  13.    SortParms obj2(std::string("SortParmsForDate"));    //The singleton itself
  14. }
  15.  
The above implementation is based on the feature that global variables are created in the order in which they are declared and that all global variables must be created before main() starts.

Based on that, you can see the registry is created followed by three singletons. These singletons register themselves using the string in the constructors.

Users must now call the Instance() method with one of those strings. The Instance() will use the string to look up the singleton in the registry and return a pointer to it.

Expand|Select|Wrap|Line Numbers
  1.  
  2. int main()
  3. {
  4.    SysParms* theSysParmsSingleton;
  5.    cout << "Example B: Creating a registered singleton" << endl;
  6.  
  7.    //
  8.    //SysParms obj;  //ERROR: singleton objects cannot be created by the user
  9.    //
  10.    //Use Instance() method to locate singleton. 
  11.    theSysParmsSingleton = SysParms::Instance("SortParms");
  12.  
  13.    //
  14.    cout << "the SortParms Singleton is located at " << theSysParmsSingleton 
  15.           << endl;
  16.  
  17.    theSysParmsSingleton = SysParms::Instance("SortParmsForPerson");
  18.  
  19.    //
  20.    cout << "the SortParmsForPerson Singleton is located at "
  21.            <<   theSysParmsSingleton << endl;
  22.  
  23.    theSysParmsSingleton = SysParms::Instance("SortParmsForDate");
  24.  
  25.    //
  26.    cout << "the SortParmsForDate Singleton is located at "
  27.           << theSysParmsSingleton << endl;
  28.  
  29.    //Set a sort sequence for Person
  30.    SysParms::Instance("SortParmsForPerson")->SetParms(SysParms::ASCENDING); 
  31.    //Set a sort sequence for Date
  32.    SysParms::Instance("SortParmsForDate")->SetParms(SysParms::DESCENDING); 
  33.  
  34.    //Retrieve sort sequence
  35.    enum SysParms::Parms rval = SysParms::Instance("SortParmsForPerson")->GetParms();
  36.    if (rval == SysParms::ASCENDING)
  37.    { 
  38.       cout << "Sort Person Ascending" << endl;
  39.    }
  40.    else
  41.    {
  42.       cout << "Sort Person Descending" << endl;
  43.    }
  44.  
  45.    //Retrieve sort sequence
  46.    rval = SysParms::Instance("SortParmsForDate")->GetParms();
  47.    if (rval == SysParms::ASCENDING)
  48.    {
  49.       cout << "Sort Date Ascending" << endl;
  50.    }
  51.    else
  52.    {
  53.       cout << "Sort Date Descending" << endl;
  54.    }
  55.    return 0;
  56. }
  57.  
Using Handles
The examples in this article use pointers since pointer syntax is commonly understood. However, it is recommended that, in a real application, handles be used. You should refer to the article on Handles in the C/C++ Articles section.

Further Information
Refer to the book Design Patterns by Erich Fromm, et al, Addison-Wesley 1994.

This article shows only the conceptual basis of the Singleton pattern but not motivations and ramifications of using this pattern.

Copyright 2007 Buchmiller Technical Associates North Bend WA USA
Jun 1 '07 #1
Share this Article
Share on Google+
3 Comments


kovik
Expert 100+
P: 1,044
The Singleton represents a replacement for a global variable. Professional C++ developers avoid global variables for a variety of reasons.
I'd just like to add that the Singleton pattern is almost as dangerous as global variables, for the same reasons, as long as you are retrieving the same object by reference.
Jul 1 '07 #2

P: 68
You should also note that the singleton pattern as presented here is not thread safe. This is an issue when a pre-emptive kernel is involved. To make a singleton thread safe, the creation of the instance needs to be protected.
Apr 30 '08 #3

weaknessforcats
Expert Mod 5K+
P: 9,197
You should also note that the singleton pattern as presented here is not thread safe.
That is correct. Thread safety is an issue only when multiple threads are involved and not all programs are multithreaded.

I felt is necessarry to stick with the pattern only.
May 5 '08 #4