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
- class SysParms
- {
- private:
- SysParms(const SysParms&); //Prevents making a copy
- protected:
- SysParms(); //Only a SysParms member can call this
- //Prevents a user from creating singleton objects
- virtual ~SysParms(); //Prevents just anyone from deleting the singleton
- public:
- //The "official" access point.
- static SysParms* Instance();
- };
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
- //Use anonymous namespace to force internal linkage for instance
- namespace
- {
- SysParms* instance = 0; //Address of the singleton
- }
- SysParms::SysParms()
- {
- }
- SysParms::~SysParms()
- {
- delete instance;
- instance = 0;
- }
- //The "official" access point
- SysParms* SysParms::Instance()
- {
- //"Lazy" initialization. Singleton not created until it's needed
- if (!instance)
- {
- instance = new SysParms;
- }
- return instance;
- }
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
- int main()
- {
- SysParms* theSysParmsSingleton;
- cout << "Example A: Creating only one instance" << endl;
- //
- //SysParms obj; //ERROR: singleton objects cannot be created by the user
- //
- //Use Instance() method to locate singleton.
- //Instance() is static as we have no object
- theSysParmsSingleton = SysParms::Instance();
- //
- cout << "theSysParmsSingleton is located at " << &theSysParmsSingleton
- << endl;
- return 0;
- }
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
- class SysParms
- {
- private:
- SysParms(const SysParms&); //Prevents making a copy
- protected:
- SysParms(); //Only a SysParms member or subclass can call this
- virtual ~SysParms(); //Prevent just anyone from deleting the singleton
- public:
- enum Parms {ASCENDING, DESCENDING};
- //The "official" access point.
- static SysParms* Instance(const std::string& name);
- //Register the singleton object by a name
- //Returns false if registration failed
- bool Register(const std::string& name, SysParms* obj);
- //Returns 0 if name not in registry
- SysParms* Lookup(const std::string& name);
- //Parameter Interface:
- //These should really be private.
- virtual Parms GetParms()= 0;
- virtual void SetParms(Parms in) = 0;
- };
Expand|Select|Wrap|Line Numbers
- extern SingletonRegistry theRegistry;
- SysParms::SysParms()
- {
- }
- SysParms::~SysParms()
- {
- }
- //The "official" access point
- //
- //You look up the singleton in theRegistry and return its address
- //
- SysParms* SysParms::Instance(const string& name)
- {
- return theRegistry.Lookup(name);
- }
- bool SysParms::Register(const string& name, SysParms* obj)
- {
- return theRegistry.Add(name, obj);
- }
- SysParms* SysParms::Lookup(const string& name)
- {
- return theRegistry.Lookup(name);
- }
Here an STL map is used for the registry.
Expand|Select|Wrap|Line Numbers
- class SingletonRegistry
- {
- private:
- std::map<std::string, SysParms*> theRegistry;
- public:
- //Default ctor and dtor of std::map<> will be used
- //Locate
- SysParms* Lookup(const std::string& name);
- //Add
- bool Add(const std::string& name, SysParms* obj);
- };
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
- extern SingletonRegistry theRegistry;
- //Registry of Singletons.
- SysParms* SingletonRegistry::Lookup(const string& name)
- {
- pair<string, SysParms*> theEntry;
- map<string, SysParms*>::iterator itr = theRegistry.find(name);
- if (itr == theRegistry.end())
- {
- return 0; //not found
- }
- return itr->second;
- }
- bool SingletonRegistry::Add(const string& name, SysParms* obj)
- {
- pair<string, SysParms*> theEntry;
- theEntry.first = name;
- theEntry.second = obj;
- theRegistry.insert(theEntry);
- return true;
- }
Expand|Select|Wrap|Line Numbers
- class SortParms :public SysParms
- {
- private:
- enum Parms seq;
- public:
- SortParms(const std::string& name);
- ~SortParms();
- virtual Parms GetParms();
- virtual void SetParms(Parms in);
- };
Expand|Select|Wrap|Line Numbers
- extern SingletonRegistry theRegistry;
- SortParms::SortParms(const std::string& name)
- {
- theRegistry.Add(name, this);
- }
- SortParms::~SortParms()
- {
- }
- enum SysParms::Parms SortParms::GetParms()
- {
- return this->seq;
- }
- void SortParms::SetParms(Parms in)
- {
- this->seq = in;
- }
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
- //The Registry object
- SingletonRegistry theRegistry;
- //
- //The SortParms objects
- //
- //The anonymous namespace makes them publicly inaccessible outside this file.
- //
- namespace
- {
- SortParms obj(std::string("SortParms")); //The singleton itself
- SortParms obj1(std::string("SortParmsForPerson")); //The singleton itself
- SortParms obj2(std::string("SortParmsForDate")); //The singleton itself
- }
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
- int main()
- {
- SysParms* theSysParmsSingleton;
- cout << "Example B: Creating a registered singleton" << endl;
- //
- //SysParms obj; //ERROR: singleton objects cannot be created by the user
- //
- //Use Instance() method to locate singleton.
- theSysParmsSingleton = SysParms::Instance("SortParms");
- //
- cout << "the SortParms Singleton is located at " << theSysParmsSingleton
- << endl;
- theSysParmsSingleton = SysParms::Instance("SortParmsForPerson");
- //
- cout << "the SortParmsForPerson Singleton is located at "
- << theSysParmsSingleton << endl;
- theSysParmsSingleton = SysParms::Instance("SortParmsForDate");
- //
- cout << "the SortParmsForDate Singleton is located at "
- << theSysParmsSingleton << endl;
- //Set a sort sequence for Person
- SysParms::Instance("SortParmsForPerson")->SetParms(SysParms::ASCENDING);
- //Set a sort sequence for Date
- SysParms::Instance("SortParmsForDate")->SetParms(SysParms::DESCENDING);
- //Retrieve sort sequence
- enum SysParms::Parms rval = SysParms::Instance("SortParmsForPerson")->GetParms();
- if (rval == SysParms::ASCENDING)
- {
- cout << "Sort Person Ascending" << endl;
- }
- else
- {
- cout << "Sort Person Descending" << endl;
- }
- //Retrieve sort sequence
- rval = SysParms::Instance("SortParmsForDate")->GetParms();
- if (rval == SysParms::ASCENDING)
- {
- cout << "Sort Date Ascending" << endl;
- }
- else
- {
- cout << "Sort Date Descending" << endl;
- }
- return 0;
- }
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