A Singleton is a design pattern that ensures a class has only one instance throughout the lifetime of a program. The single instance is shared wherever it is needed, providing centralized control over object creation.
- Prevents multiple instances of the same class from being created.
- Simplifies the management of shared resources across an application.
Example: The following program demonstrates that multiple calls return the same object.
#include <iostream>
using namespace std;
class Singleton {
public:
static Singleton& getInstance() {
static Singleton obj;
return obj;
}
private:
Singleton() {}
};
int main() {
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
cout << &s1 << endl;
cout << &s2 << endl;
return 0;
}
Output
0x404192 0x404192
Explanation: Both variables refer to the same object, so they print the same memory address.
Implementing a Singleton Class
A Singleton class prevents external object creation and provides a static function to access the only instance.
- Make the constructor private.
- Delete the copy constructor and copy assignment operator.
- Create a static function that returns the single instance.
- Ensure the instance is created only once.
Method 1: Singleton Using Meyers' Singleton (Recommended)
The Meyers' Singleton is the preferred implementation in modern C++. It uses a local static object, which is automatically initialized only once and is thread-safe since C++11.
#include <iostream>
using namespace std;
class Singleton {
private:
Singleton() {}
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton& getInstance()
{
static Singleton instance;
return instance;
}
void display()
{
cout << "Singleton Instance\n";
}
};
int main()
{
Singleton& s1 = Singleton::getInstance();
Singleton& s2 = Singleton::getInstance();
s1.display();
cout << "Address of s1: " << &s1 << endl;
cout << "Address of s2: " << &s2 << endl;
return 0;
}
Output
Singleton Instance Address of s1: 0x404192 Address of s2: 0x404192
Explanation
- The constructor is private, so objects cannot be created outside the class.
- getInstance() creates the object only during its first call.
- Every subsequent call returns the same object.
- Both variables store the address of the same instance.
Method 2: Singleton Using a Static Pointer
Another common implementation uses a static pointer that is initialized only when it is first requested. This technique is known as lazy initialization because the object is created only when needed.
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
static Singleton* getInstance()
{
if (instance == nullptr)
instance = new Singleton();
return instance;
}
void display()
{
cout << "Singleton Instance\n";
}
};
Singleton* Singleton::instance = nullptr;
int main()
{
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
s1->display();
cout << "Address of s1: " << s1 << endl;
cout << "Address of s2: " << s2 << endl;
return 0;
}
Output
Singleton Instance Address of s1: 0x416eb0 Address of s2: 0x416eb0
Explanation
- A static pointer stores the address of the Singleton object.
- During the first call to getInstance(), the object is created dynamically.
- Later calls return the same pointer without creating another object.
- Since both pointers reference the same object, they have identical addresses.
Meyers' Singleton vs Static Pointer Singleton
| Feature | Meyers' Singleton | Static Pointer Singleton |
|---|---|---|
| Initialization | Lazy | Lazy |
| Thread Safety | Yes (since C++11) | No (unless synchronized) |
| Memory Management | Automatic | Manual |
| Uses new | No | Yes |
Applications
Singleton is useful whenever a single shared object is required across the application.
- Managing application-wide configuration settings.
- Implementing logging systems.
- Managing database connections.
- Creating cache managers.
Best Practices
Follow these recommendations while implementing a Singleton.
- Prefer Meyers' Singleton in modern C++ applications.
- Delete both the copy constructor and copy assignment operator.
- Avoid manual memory management whenever possible.
- Use Singleton only when a single shared instance is actually required.