Singleton Pattern
The Singleton design pattern ensures that a class has only one instance and provides a global point of access to that instance. It is a creational design pattern that is commonly used when you want to ensure that there is only one instance of a class throughout the entire application, and that instance is shared among multiple components.
Table of Contents
Singleton Pattern C++:
#include <iostream>
class Singleton {
private:
// Private constructor to prevent direct instantiation
Singleton() {}
// The single instance of the class
static Singleton* instance;
public:
// Public method to access the single instance
static Singleton* getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
void showMessage() {
std::cout << "Hello from Singleton!" << std::endl;
}
};
// Initializing the static member variable 'instance'
Singleton* Singleton::instance = nullptr;
int main() {
// Get the single instance of Singleton
Singleton* singleton1 = Singleton::getInstance();
Singleton* singleton2 = Singleton::getInstance();
// Both 'singleton1' and 'singleton2' point to the same instance of Singleton.
singleton1->showMessage(); // Output: Hello from Singleton!
singleton2->showMessage(); // Output: Hello from Singleton!
return 0;
}
Explanation:
In the code above, we have implemented the Singleton pattern using a static method getInstance()
that returns the single instance of the Singleton
class. The constructor of the Singleton
class is made private to prevent direct instantiation of the class from outside.
The static member variable instance
is used to store the single instance of the class. When the getInstance()
method is called, it checks if the instance is already created. If not, it creates a new instance of the Singleton
class and returns it. If the instance is already created, it simply returns the existing instance.
In the main()
function, we demonstrate how to use the Singleton pattern. We call the getInstance()
method twice, and both times, it returns the same instance of the Singleton
class. Therefore, singleton1
and singleton2
point to the same memory location, confirming that there is only one instance of the Singleton
class throughout the application.
Advantages of Singleton Pattern:
- Provides a global access point to the single instance, ensuring that the instance is easily accessible from anywhere in the codebase.
- Guarantees that there is only one instance of the class, preventing multiple instantiations and reducing memory overhead.
- Supports lazy initialization, meaning the instance is created only when it is first requested, which can improve performance in certain scenarios.
Disadvantages of Singleton Pattern:
- May violate the Single Responsibility Principle by combining object creation with other responsibilities.
- Can introduce tight coupling and make testing more difficult.
- In a multi-threaded environment, special consideration is required to ensure thread-safety during instance creation.
In summary, the Singleton pattern is a useful design pattern when you want to ensure there is only one instance of a class throughout the application. By using the Singleton pattern, you can control object creation and access more effectively, promoting code reusability and reducing resource usage.
Thread Safe Singleton C++
To make the above Singleton pattern implementation thread-safe, we need to address the issue of multiple threads potentially accessing the getInstance()
method simultaneously and creating multiple instances. There are various ways to achieve thread safety in Singleton pattern implementation. One common approach is using Double-Checked Locking with Mutex. Here’s the updated code with thread safety:
#include <iostream>
#include <mutex>
class Singleton {
private:
// Private constructor to prevent direct instantiation
Singleton() {}
// The single instance of the class
static Singleton* instance;
// Mutex for thread safety
static std::mutex mtx;
public:
// Public method to access the single instance
static Singleton* getInstance() {
// Double-Checked Locking with Mutex
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(mtx); // Lock the mutex
if (instance == nullptr) {
instance = new Singleton();
}
}
return instance;
}
void showMessage() {
std::cout << "Hello from Singleton!" << std::endl;
}
};
// Initializing the static member variable 'instance'
Singleton* Singleton::instance = nullptr;
// Initializing the static member variable 'mtx'
std::mutex Singleton::mtx;
int main() {
// Get the single instance of Singleton
Singleton* singleton1 = Singleton::getInstance();
Singleton* singleton2 = Singleton::getInstance();
singleton1->showMessage(); // Output: Hello from Singleton!
singleton2->showMessage(); // Output: Hello from Singleton!
return 0;
}
Explanation:
In the updated code, we have added a static member variable mtx
, which is a std::mutex
that provides mutual exclusion to ensure thread safety. The getInstance()
method now uses Double-Checked Locking with Mutex to minimize the overhead of locking.
When a thread enters the getInstance()
method, it checks if the instance is already created (the first check). If it is not created, the thread acquires the lock on the mtx
mutex, and then rechecks the instance variable to ensure that no other thread has created the instance in the meantime (the second check). If the instance is still nullptr
, the thread creates the singleton instance. This way, we ensure that only one thread creates the instance while other threads wait for the mutex to be released.
Using the Double-Checked Locking with Mutex approach, we achieve thread safety while also minimizing the performance impact of locking by only locking when necessary.
With this modification, the Singleton pattern implementation is thread-safe, and multiple threads can safely access the getInstance()
method without creating multiple instances.