Singleton Design Pattern : Creational Pattern

Share the Article

What

Singleton design pattern is a type of creational pattern which controls the creation of the instance of a class. It mainly ensures to have only one instance of a class through out the application . It also provides access to that instance globally.

Why

Let us take an example of an application which need to access date and time of system at random places. Since time of system will be same at any instance so our requirement to create a timerClass and getDataAndTime( ). Ideally we need only one timerClass Object throughout our application.

Example without using Singleton Design Pattern

Basic approach is to a declare timerClass object as global variable and access it from multiple places. Lets see code below:

#include<iostream> //main header using namespace std; //for namespace class timerClass { public: timerClass() { cout<<"TimerClass Object created" <<endl; } void getCurrentDateandTime() { cout<<" Date is XX and Time is YY" <<endl; } }; // Default initialization timerClass *timerClassptr =NULL; // global object created void performJob1(void) { // Initialization on first use if (!timerClassptr) //need to check valdity timerClassptr = new timerClass; cout<<"Job1 1 Done at "; timerClassptr->getCurrentDateandTime(); } void performJob2(void) { if (!timerClassptr) // need to check validity timerClassptr = new timerClass; cout<<"Job2 Done at "; timerClassptr->getCurrentDateandTime(); } int main() { if (!timerClassptr) timerClassptr = new timerClass; cout<<"Main job started at"; timerClassptr->getCurrentDateandTime(); performJob1(); performJob2(); }

Output

code using global variable instead of singleton pattern

In above example we see that there is need to check for existence of the global timerClassPtr at every place. Therefore, such check ensures if it is NULL or not and then accordingly we need to construct it .

In the above approach, firstly, wherever we need to use timerClass we need to check validity of the global pointer. And secondly, we are pushing the creation logic at several places which let to code pollution. In conclusion, the application ultimately leads to creation of timerClass object from several places.

Such cases, where we need a single resource, the singleton design pattern is the best fit .

How

Lets try to update the above example with singleton design pattern. We need to follow specific rules for using this design pattern.

1. Hide the constructor of class

Firstly, the class needs to keep constructor as private or protected. This is based on the requirement that the construction of object should not possible from outside the class.

Secondly, the class also needs to make move constructor and assignment operator as private or protected in similar way.

private or protected: timerClass(); timerClass(const timerClass &); timerClass& operator=(const timerClass&);

In C++11, there is an additional requirement to make move constructor and move assignment also private.

2. Declare static & private instance inside class

Secondly, the class needs to declare an instance as static & private inside.

static timerClass *timerClassptr ;

3. Define a public function for getting instance

Thirdly, the class shall provide a static function in public scope, example, getInstance( ). This function shall take the ownership of creating and returning valid instance of the class. Any client code will use this public function to get access to class instance.

static timerClass *getInstance()

Using timerClass example with singleton design pattern

#include<iostream> //main header using namespace std; //for namespace class timerClass { private: static timerClass *timerClassptr ; timerClass() { cout<<"TimerClass Object created" <<endl; } public: static timerClass *getInstance() { if( timerClassptr == NULL) timerClassptr = new timerClass; return timerClassptr; } void getDataandTime() { cout<<" Date is XX and Time is YY" << endl; } }; // Default initialization timerClass* timerClass::timerClassptr =NULL; void performJob2(void) { timerClass::getInstance()->getDataandTime(); } void performJob1(void) { timerClass::getInstance()->getDataandTime(); } int main() { timerClass::getInstance()->getDataandTime(); performJob1(); performJob2(); }

Output

simple example of singleton design pattern

As seen above now, the singleton design pattern makes the function performJob1( ) and performJob2( ) much easier & cleaner. Therefore, there is no need to check the validity and take burden of calling constructor. Ultimately, the ownership of single instance is with singleton class. This ensures that user no longer worry about creation and maintenance of the single object.

Deleting the instance created by singleton design pattern

For singleton class, we have created timerClassptr as a static pointer inside the class. As per C++ rule, a static pointer will not by automatically cleaned-up by the destructor. In order to delete the global instance, we can follow one of below approach

A) Using a static function for deletion

Define a static function, example, deleteInstance( ) inside the class. We shall call it specifically at the end of application.

#include<iostream> //main header using namespace std; //for namespace class timerClass { private: static timerClass *timerClassptr ; timerClass() { } public: static timerClass *getInstance() { if( timerClassptr == NULL) timerClassptr = new timerClass; return timerClassptr; } void getDataandTime() { cout<<" Date is XX and Time is YY" <<endl; } static void deleteInstance() { if( timerClassptr != NULL) { delete timerClassptr; timerClassptr = NULL; } cout<< "TimerClass Object deleted" << endl; } }; // Default initialization timerClass* timerClass::timerClassptr =NULL; void performJob2(void) { timerClass::getInstance()->getDataandTime(); } void performJob1(void) { timerClass::getInstance()->getDataandTime(); } int main() { timerClass::getInstance()->getDataandTime(); performJob1(); performJob2(); timerClass::deleteInstance(); }

Output

deleting a singleton class  instance

B) In C++ 11, use smart pointers for singleton

In C++ 11, we have shared pointers which we use for managing a static instance. As per C++11 guideline of shared_pointer, this shall take care of deletion.

Please refer below example for using std::shared_ptr < >

#include<iostream> //main header #include <memory> //for smart pointers using namespace std;//for namespace class timerClass { private: static std::shared_ptr<timerClass> timerClassptr; public: static std::shared_ptr<timerClass > getInstance() { if( timerClassptr == NULL) timerClassptr = std::make_shared<timerClass>(); return timerClassptr; } void getDataandTime() { cout<<" Date is XX and Time is YY" <<endl; } ~timerClass() { cout << "~timerClass" << endl; } }; // Default initialization std::shared_ptr<timerClass> timerClass::timerClassptr=NULL; void performJob2(void) { timerClass::getInstance()->getDataandTime(); } void performJob1(void) { timerClass::getInstance()->getDataandTime(); } int main() { std::shared_ptr<timerClass> s1= timerClass::getInstance(); s1.get()->getDataandTime(); performJob1(); performJob2(); }

Output

using singleton design pattern with smart pointers

Using singleton design pattern in a multithreaded application

Till now we have seen singleton class application with only one main thread. In order to support singleton class for a multithreaded application, we need to take care of Race Condition also.

Lets update now the timerClass application for enabling two threads performing job1 and job2 in parallel along with the main thread . We need to synchronize the creation and deletion of the instances so that thread can perform their jobs as expected.

#include<iostream> //main header #include<thread> //for threads #include<mutex> //for mutex using namespace std;//for namespace std::mutex m1; class timerClass { private: static timerClass *timerClassptr ; timerClass() { } public: static timerClass *getInstance() { m1.lock(); if( timerClassptr == NULL) timerClassptr = new timerClass; m1.unlock(); return timerClassptr; } void getDataandTime() { cout<<" Date is XX and Time is YY" <<endl; } static void deleteInstance() { m1.lock(); if( timerClassptr != NULL) { delete timerClassptr; timerClassptr = NULL; } m1.unlock(); cout<<" TimerClass Object deleted" <<endl; } }; // Default initialization timerClass* timerClass::timerClassptr =NULL; void performJob2(void) { timerClass::getInstance()->getDataandTime(); } void performJob1(void) { timerClass::getInstance()->getDataandTime(); } int main() { timerClass::getInstance()->getDataandTime(); std::thread mythread1 (performJob1); std::thread mythread2 (performJob2); mythread1.join(); mythread2.join(); timerClass::deleteInstance(); }

Output

code using singleton design pattern with multithreading

Pros & Cons of Singleton Design Pattern

Pros

  1. The singleton design pattern creates manageable code for classes who need only one instance.
  2. Singleton class takes care of creation of instance. The user need not take the burden of managing of the instance.
  3. There is no need for user to check validity of instance.
  4. This can be used efficiently in Multithreaded application also.

Cons

  1. The introduction of a static instance into the system can make unit testing difficult.
  2. Us of too many singleton classes in one application will pollute the code.
  3. User need to take care of deletion of objects at the end of application.

Main Funda: Singleton pattern is useful for managing critical resources.

Advanced C++ Topics

Abstract Factory Design Pattern
Class Template Argument Deduction in C++17
What is a Tuple, a Pair and a Tie in C++
C++ Multithreading: Understanding Threads
What is Copy Elision, RVO & NRVO?
Lambda in C++11
Lambda in C++17
std::chrono in C++ 11
Thread Synchronization with Mutex
Template type deduction in functions
How std::forward( ) works?
How std::move() function works?
What is reference collapsing?

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *