Да.. Мы знаем, что не стоит использовать этот паттерн и сколько бы эту тему не прочесали до меня, я, пожалуй, вставлю свое слово. А именно, слово про реализацию. Хоть я и не скажу ничего нового, но как минимум зафиксирую этот момент для себя.
Чтож.. В поиске реализаций класса Singleton мы обычно обращаемся к статьям, книгам или ищем примеры на гитхабе и, в итоге, натыкаемся на что-то подобное:
И обычно сходу берем его на вооружение.
Да. Это типичный пример singleton-а. И на самом деле ничего сверхплохого в данной реализации нет. Но через какое-то время в этом примере можно увидеть некоторый изъян реализации.
Давайте рассмотрим следующую ситуацию. Вы пишете программу. И в вашей программе должен быть кот. И кот должен быть один. Совсем один. И вы решаете делать его классом одиночкой. Например, по вышеприведенному шаблону. Некоторый дискомфорт можно почувствовать при попытке придумать название класса. Ведь просто Cat не подойдет, это ведь кот одиночка. А что тогда подойдет? SingleCat? LonelyKitten?
К тому же, зная про такую штуку как SOLID мы увидим, что этот класс будет противоречить первому принципу. А именно The Single Responsibility Principle (Принцип единственной ответственности). То есть наш класс будет не только котом, но еще и одиночкой.
А во вторых, у нас может возникнуть потребность создать собаку, гуся, корову и чтобы все они были синглтонами. Или чтобы из трех собак только одна собака была синглтоном, а остальные были обычными.
И что нам нужно делать? Правильно! Делить ответственность! Прибегая к помощи шаблонов создаем класс Singleton, instance которого будет иметь шаблонный тип. И выглядеть это может примерно следующим образом.
И в будущем при реализации кота мы будем думать только про кота, не заботясь о том, как делать его синглтоном.
А сделать так, чтобы появился кот синглтон (или собака) проще простого.
С таким же успехом можно создать одиночный класс std::string или, например, std::vector.
Зачем? Это просто пример! В реальной жизни это может быть класс для подключения к базе данных.