Ответ
В C++ проектах я применял Singleton крайне редко и осознанно, только для объектов, которые действительно должны существовать в единственном экземпляре на протяжении всей жизни программы и быть доступными из разных частей кода. Основные сценарии:
- Логирование (Logger): Глобальный логгер, в который пишут все модули.
- Доступ к конфигурации приложения: Единый объект, загружающий настройки из файла.
- Менеджер ресурсов: Например, кэш текстур или пул соединений с базой данных.
Реализация в современном C++ (потокобезопасная, с отложенной инициализацией):
class DatabaseConnectionPool {
private:
DatabaseConnectionPool() { /* установка соединений */ } // Приватный конструктор
~DatabaseConnectionPool() = default;
// Запрещаем копирование и присваивание
DatabaseConnectionPool(const DatabaseConnectionPool&) = delete;
DatabaseConnectionPool& operator=(const DatabaseConnectionPool&) = delete;
public:
static DatabaseConnectionPool& getInstance() {
// Потокобезопасно начиная с C++11
static DatabaseConnectionPool instance;
return instance;
}
Connection getConnection() { /* ... */ }
void returnConnection(Connection conn) { /* ... */ }
};
// Использование
auto& pool = DatabaseConnectionPool::getInstance();
auto conn = pool.getConnection();
Важно понимать недостатки: Singleton создает скрытые глобальные зависимости, что усложняет модульное тестирование. Часто лучшей альтернативой является передача зависимости через конструктор (Dependency Injection). Я использую Singleton только тогда, когда его преимущества перевешивают эти недостатки, и объект по своей природе является истинно глобальным.