Singleton Pattern Implementation in C++
Singleton Pattern Fundamentals
The Singleton pattern ensures a class has only one global instance while providing controlled access to it. This pattern is suitable for scenarios requiring centralized management of shared resources or global configuration settings.
Lazy Initialization Approach
class Logger {
public:
static Logger* getShared() {
if (shared_logger == nullptr) {
shared_logger = new Logger();
}
return shared_logger;
}
private:
Logger() = default;
~Logger() = default;
static Logger* shared_logger;
};
class Settings {
public:
static Settings& obtain() {
static Settings config;
return config;
}
private:
Settings() = default;
~Settings() {}
};
These implementations restrict instantiation through private constructors. The first example uses pointer-based delayed initialization, while the second leverages C++11 static local variables for automatic singleton management.
Eager Initialization Method
// Configuration.h
class Configuration {
public:
static Configuration& fetch() {
return global_config;
}
private:
Configuration() = default;
~Configuration() {}
static Configuration global_config;
};
// Configuration.cpp
Configuration Configuration::global_config;
Eager initialization creates the instance during static initialization, eliminating runtime instantiation overhead and thread-safety concerns.
Thread Safety Considerations
// C++98 Thread-safe Implementation
class DatabasePool {
public:
static DatabasePool* access() {
if (db_instance == nullptr) {
std::lock_guard<std::mutex> guard(pool_mutex);
if (db_instance == nullptr) {
db_instance = new DatabasePool();
}
}
return db_instance;
}
private:
static DatabasePool* db_instance;
static std::mutex pool_mutex;
};
// C++11 Thread-safe Implementation
class CacheManager {
public:
static CacheManager& retrieve() {
std::call_once(init_flag, [] {
cache_instance.reset(new CacheManager);
});
return *cache_instance;
}
private:
static std::unique_ptr<CacheManager> cache_instance;
static std::once_flag init_flag;
};
These implementations address concurrnet access issues. The C++98 version uses double-checked locking, while the C++11 approach employs std::call_once for initialization safety.
Pattern Comparison
Lazy initialization creates instances on first access, conserving memory but requiring thread synchronization. Eager initialization allocates resources upfront, simplifying implementation but potentially wasting memory.
Practical Applications
- Resource management: Centralized handling of database connections or thread pools
- Configuration control: Global access to application settings
- System services: Unified logging mechanisms accessible throughout an application