Article by Ayman Alheraki on January 11 2026 10:33 AM
Introduction
The Singleton pattern is one of the fundamental design patterns in software engineering, often used to ensure that a class has only one instance and provides a global point of access to that instance. This pattern is particularly useful in scenarios where a single shared resource or service needs to be controlled across the entire application. In this article, we'll explore the Singleton pattern in C++, its implementation, variations, and best practices.
The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance. This pattern is used when exactly one instance of a class is required to coordinate actions across the system. Examples include configuration managers, logging services, and database connections.
Single Instance: Only one instance of the class is created.
Global Access: The instance is accessible globally throughout the application.
Controlled Access: The creation of the instance is controlled to prevent multiple instantiations.
The basic implementation of the Singleton pattern involves the following steps:
Private Constructor: Ensure the constructor is private to prevent instantiation from outside the class.
Static Instance: Use a static member variable to hold the instance.
Public Accessor: Provide a public static method to get the instance.
Here’s a simple example:
class Singleton {private: static Singleton* instance; // Private constructor to prevent instantiation Singleton() {}
public: // Public static method to access the singleton instance static Singleton* getInstance() { if (instance == nullptr) { instance = new Singleton(); } return instance; }
void showMessage() { std::cout << "Singleton instance accessed!" << std::endl; }};
// Initialize static memberSingleton* Singleton::instance = nullptr;
int main() { Singleton* singleton = Singleton::getInstance(); singleton->showMessage(); return 0;}In a multithreaded environment, the basic implementation might lead to race conditions where multiple threads create multiple instances of the Singleton. To make the Singleton pattern thread-safe, we can use a mutex to synchronize access.
Here’s how you can implement a thread-safe Singleton:
class Singleton {private: static Singleton* instance; static std::mutex mtx; // Private constructor to prevent instantiation Singleton() {}
public: // Public static method to access the singleton instance static Singleton* getInstance() { if (instance == nullptr) { std::lock_guard<std::mutex> lock(mtx); // Lock to ensure thread safety if (instance == nullptr) { instance = new Singleton(); } } return instance; }
void showMessage() { std::cout << "Singleton instance accessed!" << std::endl; }};
// Initialize static membersSingleton* Singleton::instance = nullptr;std::mutex Singleton::mtx;
int main() { Singleton* singleton = Singleton::getInstance(); singleton->showMessage(); return 0;}The above implementations use lazy initialization, meaning the instance is created only when it is needed. This can be further improved by using a local static variable inside the accessor method, which automatically handles thread safety in C++11 and later.
Here’s an updated implementation using local static variable:
class Singleton {private: // Private constructor to prevent instantiation Singleton() {}
public: // Public static method to access the singleton instance static Singleton* getInstance() { static Singleton instance; // Local static variable return &instance; }
void showMessage() { std::cout << "Singleton instance accessed!" << std::endl; }};
int main() { Singleton* singleton = Singleton::getInstance(); singleton->showMessage(); return 0;}Modern C++ encourages the use of smart pointers for better resource management. We can implement the Singleton pattern using std::unique_ptr to ensure proper deletion and avoid manual memory management.
Here’s an example using std::unique_ptr:
class Singleton {private: static std::unique_ptr<Singleton> instance; // Private constructor to prevent instantiation Singleton() {}
public: // Public static method to access the singleton instance static Singleton* getInstance() { if (!instance) { instance.reset(new Singleton()); } return instance.get(); }
void showMessage() { std::cout << "Singleton instance accessed!" << std::endl; }};
// Initialize static memberstd::unique_ptr<Singleton> Singleton::instance = nullptr;
int main() { Singleton* singleton = Singleton::getInstance(); singleton->showMessage(); return 0;}The double-checked locking pattern reduces the overhead of acquiring a lock by first checking if the instance is already created before acquiring the lock.
class Singleton {private: static Singleton* instance; static std::mutex mtx; // Private constructor to prevent instantiation Singleton() {}
public: // Public static method to access the singleton instance static Singleton* getInstance() { if (instance == nullptr) { std::lock_guard<std::mutex> lock(mtx); if (instance == nullptr) { instance = new Singleton(); } } return instance; }
void showMessage() { std::cout << "Singleton instance accessed!" << std::endl; }};
// Initialize static membersSingleton* Singleton::instance = nullptr;std::mutex Singleton::mtx;
int main() { Singleton* singleton = Singleton::getInstance(); singleton->showMessage(); return 0;}In some cases, you might need to inject dependencies into the Singleton. This can be done by modifying the design to support dependency injection.
class Database {public: void connect() { std::cout << "Connected to database!" << std::endl; }};
class Singleton {private: static std::unique_ptr<Singleton> instance; static std::mutex mtx; std::shared_ptr<Database> db;
// Private constructor to prevent instantiation Singleton(std::shared_ptr<Database> database) : db(database) {}
public: // Public static method to access the singleton instance static Singleton* getInstance(std::shared_ptr<Database> database) { if (!instance) { std::lock_guard<std::mutex> lock(mtx); if (!instance) { instance.reset(new Singleton(database)); } } return instance.get(); }
void showMessage() { db->connect(); std::cout << "Singleton instance accessed with database dependency!" << std::endl; }};
// Initialize static membersstd::unique_ptr<Singleton> Singleton::instance = nullptr;std::mutex Singleton::mtx;
int main() { auto db = std::make_shared<Database>(); Singleton* singleton = Singleton::getInstance(db); singleton->showMessage(); return 0;}Ensure Thread Safety: Use thread-safe mechanisms if the Singleton is to be used in a multithreaded environment.
Avoid Global Variables: Minimize the use of global state to avoid hidden dependencies and improve testability.
Consider Resource Management: Use smart pointers to manage resources and avoid memory leaks.
Singleton for Purposeful Use: Only use Singleton where it makes sense, such as for configuration management or logging services, to avoid misuse.
The Singleton pattern is a powerful design pattern used to ensure a single instance of a class and provide global access to that instance. In C++, implementing the Singleton pattern involves careful consideration of thread safety, resource management, and design choices. By understanding and applying the Singleton pattern effectively, you can manage shared resources efficiently and maintain cleaner, more organized code in your C++ applications.