Когда стоит использовать паттерн Singleton?

«Когда стоит использовать паттерн Singleton?» — вопрос из категории Паттерны, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В C++ проектах я применял Singleton крайне редко и осознанно, только для объектов, которые действительно должны существовать в единственном экземпляре на протяжении всей жизни программы и быть доступными из разных частей кода. Основные сценарии:

  1. Логирование (Logger): Глобальный логгер, в который пишут все модули.
  2. Доступ к конфигурации приложения: Единый объект, загружающий настройки из файла.
  3. Менеджер ресурсов: Например, кэш текстур или пул соединений с базой данных.

Реализация в современном 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 только тогда, когда его преимущества перевешивают эти недостатки, и объект по своей природе является истинно глобальным.