История вопроса:
Шаблон Singleton был предложен для ограничения создания только одного экземпляра определенного класса, что было востребовано для реализации глобальных менеджеров (например, логгеров, пулов ресурсов, настройщиков).
Проблема:
Реализация Singleton кажется простой, но в C++ возникают трудности: потокобезопасность, корректность разрушения, порядок инициализации.
Решение:
Наиболее безопасный и современный способ реализовать Singleton использует статическую локальную переменную внутри статической функции, что гарантирует инициализацию при первом обращении, потокобезопасность (начиная с C++11) и корректное разрушение.
Пример кода:
class Singleton { public: static Singleton& instance() { static Singleton s; return s; } void doSomething() {} private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
Ключевые особенности:
Можно ли создать второй экземпляр Singleton с помощью сериализации или клонирования?
Да. Если реализовать методы сериализации/десериализации или вручную реализовать clone(), не ограничив конструктор копирования, может появиться второй экземпляр. Во избежание этого требуется запретить все способы копирования, клонирования и восстановления через сериализацию.
Будет ли корректно реализован Singleton в многопоточной среде в C++98/03 стандартом через локальную статическую переменную?
Нет. Локальные статические переменные до C++11 не гарантировали потокобезопасность при инициализации. Это могло привести к созданию нескольких экземпляров если два потока одновременно попадали в функцию instance(). В C++11 и новее – проблема решена на уровне стандарта.
Когда уничтожается экземпляр Singleton, создаваемый через статическую локальную переменную?
Объект уничтожается в порядке, обратном созданию (LIFO) на этапе завершения программы (exit). Но это может привести к проблемам, если в деструкторе обращаются к объектам, уже разрушенным.
В системе логирования разработчик реализует Singleton с помощью глобального указателя и new, забывая запретить копирование. Программа работает, но в многопоточной среде иногда получаются разные экземпляры логгера, сообщения теряются.
Плюсы:
Минусы:
Singleton реализован через статическую локальную переменную в статической функции, запрещено копирование. Потокобезопасность обеспечена, программа масштабируется, логи корректно разделяются.
Плюсы:
Минусы: