Редактировать: Из другого вопроса я дал ответ, в котором есть ссылки на множество вопросов / ответов о синглтонах: Подробнее о синглтонах здесь:
Итак, я прочитал ветку Синглтоны: хороший дизайн или костыль?
И спор все еще продолжается.
Я рассматриваю синглтоны как образец дизайна (хороший и плохой). Проблема с синглтоном не в шаблоне, а в пользователях (извините всех). Все и их отец думают, что могут правильно его реализовать (и, судя по многочисленным интервью, которые я провел, большинство людей не могут). Кроме того, поскольку все думают, что могут реализовать правильный синглтон, они злоупотребляют шаблоном и используют его в неподходящих ситуациях (заменяя глобальные переменные на синглтоны!).
Итак, основные вопросы, на которые необходимо ответить:
Я надеюсь, что в этой статье мы сможем собрать в одном месте (вместо того, чтобы гуглить и искать на нескольких сайтах) авторитетный источник того, когда (и как) правильно использовать синглтон. Также уместным будет список Anti-Usages и распространенных плохих реализаций, объясняющий, почему они не работают, а для хороших реализаций - их слабые места.
Так что вперед:
Я подниму руку и скажу, что это то, что я использую, но, вероятно, у меня есть проблемы.
Мне нравится, как Скотт Майерс рассматривает эту тему в его книгах «Эффективный C++».
Good Situations to use Singletons (not many):
- Logging frameworks
- Thread recycling pools
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
OK. Давайте вместе соберем некоторую критику и другие реализации. :-)
Кто сказал, что у фреймворка может быть только 1 экземпляр регистратора. В синглтоне, представляющем Framework. Затем Framework может предоставить вам определенные регистраторы.
Да. Я бы не стал использовать одиночную песню в качестве катушки с нитками. Просто выдвигайте идеи, чтобы получить ответы.
@Dan Singleton, реализующий шаблон стратегии. Поведение абстрагируется от синглтона. Синглтон - это единая точка входа. Не используйте два регистратора, имейте один регистратор, который может решать, как вести журнал. Вы не можете выводить данные только в один журнал за один раз, нет необходимости иметь два.
Xaade: что, если вы хотите войти в два файла? Или в базу данных? Или сетевая розетка? Или виджет GUI? Дело в том, что не добавляйте искусственных ограничений - в этом нет необходимости. Как часто вы случайно создавали два цикла for вместо одного? Если вам нужен только один регистратор, создайте только один.





Первый пример небезопасен для потоков - если два потока одновременно вызывают getInstance, этот статический объект будет PITA. Может помочь какая-то форма мьютекса.
Да, это отмечено в комментариях выше: * Ограничение: однопоточная конструкция * См .: aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf * Проблемы, связанные с блокировкой в многопоточных приложениях
Классический синглтон с только getInstance в качестве статического метода и методов экземпляра для других операций никогда не может быть потокобезопасным. (ну, если вы не сделаете его одним на тонну потока, используя локальное хранилище потоков ...)
даже в C++ 11 или новее?
Синглтоны в основном позволяют вам иметь сложное глобальное состояние в языках, которые в противном случае затрудняют или делают невозможным создание сложных глобальных переменных.
В Java, в частности, используются синглтоны в качестве замены глобальных переменных, поскольку все должно содержаться в классе. Наиболее близкими к глобальным переменным являются общедоступные статические переменные, которые можно использовать, как если бы они были глобальными с import static.
В C++ есть глобальные переменные, но порядок, в котором вызываются конструкторы глобальных переменных класса, не определен. Таким образом, синглтон позволяет отложить создание глобальной переменной до тех пор, пока эта переменная не понадобится в первый раз.
Такие языки, как Python и Ruby, очень мало используют синглтоны, потому что вместо этого вы можете использовать глобальные переменные внутри модуля.
Итак, когда хорошо / плохо использовать синглтон? Практически точно, когда было бы хорошо / плохо использовать глобальную переменную.
Когда когда-либо глобальная переменная оказывается «хорошей»? Иногда они являются лучшим решением проблемы, но они никогда не бывают «хорошими».
Глобальная переменная хороша, когда используется везде, и все может иметь к ней доступ. Реализация машины Тьюринга с одним состоянием может использовать синглтон.
Мне нравится уровень косвенности в этом ответе: «когда было бы хорошо / плохо использовать глобальный». И DevSolar, и Ли Лувьер получают то значение, с которым они согласны, хотя на момент ответа еще не могло быть известно, кто будет комментировать.
Антииспользование:
Одна из основных проблем, связанных с чрезмерным использованием синглтонов, заключается в том, что шаблон препятствует простому расширению и замене альтернативных реализаций. Имя класса жестко запрограммировано везде, где используется синглтон.
Голосование отклонено по 2 причинам: 1. Синглтон может внутренне использовать полиморфные экземпляры (например, global Logger использует полиморфные стратегии таргетинга) 2. Для одноэлементного имени может быть typedef, поэтому код фактически зависит от typedef.
В итоге я создал свою версию синглтона, которую можно было бы расширить с помощью любопытно повторяющегося шаблона шаблона.
Одно дело с паттернами: не обобщайте. У них есть все случаи, когда они полезны, а когда терпят неудачу.
Синглтон может быть неприятным, когда вам нужно тест кода. Обычно вы застреваете в одном экземпляре класса и можете выбирать между открытием двери в конструкторе или каким-либо методом для сброса состояния и так далее.
Другая проблема заключается в том, что синглтон на самом деле представляет собой не что иное, как замаскированный глобальная переменная. Когда у вас слишком много глобального общего состояния для вашей программы, все имеет тенденцию возвращаться, мы все это знаем.
Это может усложнить отслеживание зависимостей. Когда все зависит от вашего синглтона, его сложнее изменить, разделить на два и т. д. Обычно вы застреваете на этом. Это также снижает гибкость. Изучите некоторую структуру Внедрение зависимости, чтобы попытаться решить эту проблему.
Нет, синглтон - это намного больше, чем замаскированная глобальная переменная. Вот что делает его особенно плохим. Он сочетает в себе глобальность (что обычно плохо) с другой концепцией, которая является плохой также (не позволять программисту создавать экземпляр класса, если он решает, что ему нужен экземпляр). Они часто являются использовал в качестве глобальных переменных, да. А затем они втягивают в себя и другой неприятный побочный эффект и наносят вред кодовой базе.
Также следует отметить, что одиночные игры не обязательно должны быть общедоступными. Синглтон вполне может быть внутренним по отношению к библиотеке и никогда не отображаться пользователю. Так что они не обязательно «глобальные» в этом смысле.
@jalf Не позволять кому-либо создавать более одного экземпляра класса - это неплохо. Если действительно существует только один экземпляр класса, который обеспечивает выполнение требования. Если кто-то позже решит, что ему нужно создать еще один экземпляр, он должен его реорганизовать, потому что он никогда не должен был быть синглтоном.
@William: как только что-то делается синглтоном, рефакторинг становится невероятно трудным. Теперь, можете ли вы назвать мне единственную причину, по которой было бы хорошей идеей ввести такое ограничение «только один экземпляр»? Пример Один ситуации, когда это, несомненно, правильный поступок?
@jalf В целом я согласен с вами, но в РЕДКИХ случаях все в порядке. Если вы думаете, что вам нужен синглтон, скорее всего, вы этого не сделаете. Были времена, когда мне приходилось создавать и разрушать объект несколько раз, и внутри этого объекта требовалось много журналов. Я сделал класс ведения журнала одноэлементным, чтобы мне не пришлось добавлять еще один объект, который я много создавал и разрушал. Не все ни правильно, ни неправильно, даже в программировании есть оттенки серого.
@William: и время от времени мне приходилось иметь несколько регистраторов. Вы не за синглтон, а за простого старого местного жителя. Вы хотите знать, что регистратор а всегда доступен. Вот для чего нужен глобал. Вам не нужно знать, что ни один другой регистратор не может быть создан, который принудительно выполняет синглтон. (попробуйте написать модульные тесты для вашего регистратора - это намного проще, если вы можете создавать и уничтожать его по мере необходимости, а это невозможно с синглтоном)
Конечно, вы правы, редко бывает черный или белый. Но это не оправдание для того, чтобы придерживаться плохих идей. Я с радостью допущу, что существует ситуация мог бы, в которой требуется одноэлемент. Но «мне нужно знать, что доступен глобально доступный регистратор» - это не такой случай.
Кстати, ваше второе предложение - это обобщение (от чего предостерегает ваше первое предложение). Почему вы думаете, что у них все есть случаи, когда они полезны? :)
Я думаю, что это самая надежная версия для C#:
using System;
using System.Collections;
using System.Threading;
namespace DoFactory.GangOfFour.Singleton.RealWorld
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Same instance?
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 server requests
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// "Singleton"
class LoadBalancer
{
private static LoadBalancer instance;
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Lock synchronization object
private static object syncLock = new object();
// Constructor (protected)
protected LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
// Support multithreaded applications through
// 'Double checked locking' pattern which (once
// the instance exists) avoids locking each
// time the method is invoked
if (instance == null)
{
lock (syncLock)
{
if (instance == null)
{
instance = new LoadBalancer();
}
}
}
return instance;
}
// Simple, but effective random load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
Вот Версия, оптимизированная для .NET:
using System;
using System.Collections;
namespace DoFactory.GangOfFour.Singleton.NETOptimized
{
// MainApp test application
class MainApp
{
static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();
// Confirm these are the same instance
if (b1 == b2 && b2 == b3 && b3 == b4)
{
Console.WriteLine("Same instance\n");
}
// All are the same instance -- use b1 arbitrarily
// Load balance 15 requests for a server
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}
// Wait for user
Console.Read();
}
}
// Singleton
sealed class LoadBalancer
{
// Static members are lazily initialized.
// .NET guarantees thread safety for static initialization
private static readonly LoadBalancer instance =
new LoadBalancer();
private ArrayList servers = new ArrayList();
private Random random = new Random();
// Note: constructor is private.
private LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}
public static LoadBalancer GetLoadBalancer()
{
return instance;
}
// Simple, but effective load balancer
public string Server
{
get
{
int r = random.Next(servers.Count);
return servers[r].ToString();
}
}
}
}
Вы можете найти этот шаблон на dotfactory.com.
Вы можете вырезать части, не относящиеся конкретно к синглетонам, чтобы код было легче читать.
Кроме того, ваша первая версия не является потокобезопасной из-за возможного переупорядочения чтения / записи. См. stackoverflow.com/questions/9666/…
Эээ ... неправильный язык? Вопрос, очевидно, помечен как C++.
Проблема синглтонов не в их реализации. Дело в том, что они объединяют два разных понятия, ни одно из которых явно не желательно.
1) Синглтоны обеспечивают глобальный механизм доступа к объекту. Хотя они могут быть немного более потокобезопасными или немного более надежными в языках без четко определенного порядка инициализации, это использование по-прежнему является моральным эквивалентом глобальной переменной. Это глобальная переменная, облаченная в какой-то неудобный синтаксис (например, foo :: get_instance () вместо g_foo), но она служит той же цели (единый объект, доступный для всей программы) и имеет те же недостатки.
2) Синглтоны предотвращают создание нескольких экземпляров класса. Редко, IME, что такая функция должна быть встроена в класс. Обычно это гораздо более контекстная вещь; Многие вещи, которые считаются уникальными, на самом деле просто случайны. ИМО более подходящее решение - просто создать только один экземпляр - пока вы не поймете, что вам нужно более одного экземпляра.
Согласовано. По мнению некоторых, в реальном мире два заблуждения могут дать добро. Но в программировании смешение двух плохих идей не приводит к хорошей.
Синглтоны удобны, когда у вас много кода, запускаемого при инициализации и объекте. Например, когда вы используете iBatis при настройке объекта сохранения, он должен прочитать все конфигурации, проанализировать карты, убедиться, что все правильно, и т. д., Прежде чем переходить к вашему коду.
Если бы вы делали это каждый раз, производительность сильно упала бы. Используя его в синглтоне, вы принимаете это попадание один раз, и тогда все последующие вызовы не должны этого делать.
Образец прототипа также делает это, и он более гибкий. Вы также можете использовать его, когда ваш клиент создаст много экземпляров вашего дорогостоящего класса, но только ограниченное количество из них действительно имеет другое состояние. Например, тетронимы в тетрисе.
Настоящая беда синглтонов состоит в том, что они нарушают наследование. Вы не можете создать новый класс, чтобы предоставить вам расширенную функциональность, если у вас нет доступа к коду, в котором есть ссылка на Singleton. Таким образом, помимо того, что синглтон сделает ваш код тесно связанным (исправляемым с помощью шаблона стратегии ... он же Dependency Injection), он также не позволит вам закрыть разделы кода от ревизии (общие библиотеки).
Таким образом, даже примеры средств ведения журнала или пулов потоков недействительны и должны быть заменены стратегиями.
Сами лесорубы не должны быть одиночками. Общая "широковещательная" система обмена сообщениями должна быть. Сами регистраторы являются подписчиками широковещательных сообщений.
Пулы потоков также не должны быть одиночными. Общий вопрос: захотите ли вы когда-нибудь больше одного из них? Да. Когда я последний раз их использовал, у нас было 3 разных пула потоков в одном приложении.
В настольных приложениях (я знаю, что их пишут только мы, динозавры!) Они необходимы для получения относительно неизменных глобальных настроек приложения - языка пользователя, пути к файлам справки, пользовательских настроек и т. д., Которые в противном случае должны были бы распространяться в каждый класс и каждый диалог. .
Изменить - конечно, они должны быть доступны только для чтения!
Но это напрашивается вопрос; почему язык пользователя и путь к файлу справки должны быть экземплярами метода вообще?
Для этого у нас есть глобалы. Нет необходимости делать их одиночными
Глобальные переменные - тогда как их сериализовать из реестра / базы данных? Глобальный класс - тогда как вы гарантируете, что существует только один из них?
@mgb: вы их сериализуете, считывая значения из реестра / базы данных и сохраняя их в глобальных переменных (вероятно, это должно быть сделано в верхней части вашей основной функции). вы гарантируете, что существует только один объект класса, создавая только один объект класса ... действительно ... это так сложно 'grep -rn "new \ + global_class_name".' ? В самом деле?
@mgb: С какой стати я должен гарантировать, что есть только один? Мне просто нужно знать, что один экземпляр всегда представляет настройки Текущий. но нет причин, по которым мне не должно быть позволено иметь поблизости другие объекты настроек. Например, один для «настроек, которые пользователь в настоящее время определяет, но еще не применил». Или один для «конфигурации, которую пользователь сохранил ранее, чтобы он мог вернуться к ним позже». Или по одному для каждого из ваших модульных тестов.
Я использую Singletons как тест на собеседовании.
Когда я прошу разработчика назвать несколько шаблонов проектирования, если все, что они могут назвать, - это синглтон, их не нанимают.
Жесткие и быстрые правила найма заставят вас упустить широкий круг потенциальных сотрудников.
Существует множество идиотов. Это не означает, что их следует рассматривать при приеме на работу. Если кто-то может вообще не упомянуть шаблоны проектирования, я думаю, что они будут предпочтительнее, чем кто-то, кто знает синглтон и никакие другие шаблоны.
Что касается книги рекордов - мой ответ был насмешливым. В моем собственном процессе собеседования я пытаюсь оценить, нужно ли нам обучать кого-то C++ и насколько это будет сложно. Некоторые из моих любимых кандидатов - это люди, которые НЕ ЗНАЮТ C++ насквозь, но я смог отлично поговорить с ними об этом.
Голосование против. По моему личному опыту - программист, возможно, не сможет назвать какие-либо другие шаблоны, кроме синглтонов, но это не значит, что он использует синглтоны. Лично я использовал синглтоны в своем коде ДО того, как я когда-либо о них услышал (я назвал их «умнее глобалы» - я знал, что такое глобальные). Когда я узнал о них, когда узнал об их плюсах и минусах - я перестал их использовать. Внезапно, когда я остановился, модульное тестирование стало для меня намного интереснее ... Из-за этого я стал худшим программистом? Пффф ...
Голосование против. Хороший программист знает хорошие и плохие практики. Он читал Мейерса и Александреску и все те статьи, в которых говорится, что одиночные игры - зло (в большинстве случаев). Синглтон - очень глубокая тема. Если программист это знает, это хорошо.
Я также голосую против бессмысленного вопроса "назовите несколько шаблонов проектирования". Проектирование - это понимание того, как применять шаблоны проектирования, а не просто умение наматывать их названия. Хорошо, это может не служить основанием для отрицательного голоса, но этот ответ тролльский.
Голосовать против. Если я подам заявку как разработчик C++ и найду особенность Design Patterns, которая занимает видное место на собеседовании, я знаю, что не хочу там работать.
Шаблоны проектирования @DevSolar важны, но знание того, как их применять, гораздо важнее умения назвать список. Кто-то может идеально разрабатывать дизайн, используя все правильные шаблоны проектирования для правильных ситуаций, и не знать их по имени.
@CashCow: Они находятся важны хотя бы потому, что общаются с другими разработчиками. Но «классические» паттерны предназначены для «чистого» OOD ...
@DevSolar Полностью согласен с вами. Я вышел из интервью, на которое я ехал почти 2 часа, потому что первым вопросом был: «Вы знаете, что такое синглтон?» Я ответил «да, конечно ...» и очень растерялся. Они попросили меня написать одну на ноутбуке. Я встал, поблагодарил их за потраченное время и ушел, весьма оскорбленный. Печально то, что интервьюер был «ведущим разработчиком» Kobo Books здесь, в Онтарио.
Не думаю, что это хороший тест для приема на работу. Какая у тебя цель? Вам нужен кто-то, у кого есть опыт работы с шаблонами дизайна? Если да, вы должны заявить об этом в необходимом опыте и попросить их реализовать или описать шаблон в дополнение к простому названию. Если вам довелось использовать шаблон синглтон в производстве (и по уважительной причине, а не только потому), и вам нужен кто-то, кто может реализовать шаблон синглтона, просто опишите его им и попросите его реализовать. Как бы то ни было, вы оба проигрываете с вашей текущей стратегией найма.
Голосование против. Это бесполезно для этого разговора и кажется довольно ограниченным подходом к собеседованию.
По моему опыту, ответ на этот вопрос является довольно хорошим показателем глубины знаний. Если синглтон - единственный паттерн, который может назвать собеседник, и он претендует на звание «старшего» разработчика ...
lmao, который кажется не очень хорошим / точным тестом на навыки программирования, если только вы не ищете людей, которые знают только шаблоны проектирования. По моему личному опыту, хорошим кандидатом в программисты отличает его способность не только обсуждать концепции кодирования, но и связывать их с основной бизнес-моделью компании.
Но когда мне нужно что-то вроде синглтона, я часто использую Счетчик Шварца для его создания.
Современный дизайн C++ от Alexandrescu имеет потокобезопасный наследуемый универсальный синглтон.
Я считаю, что для моей 2p-ценности важно определить время жизни ваших синглтонов (когда их использовать абсолютно необходимо). Обычно я не позволяю статической функции get() создавать что-либо и оставляю настройку и уничтожение некоторому выделенному разделу основного приложения. Это помогает выделить зависимости между синглтонами, но, как подчеркивалось выше, лучше просто избегать их, если это возможно.
Отвечать:
Используйте синглтон, если:
Не используйте синглтон, если:
Как создать лучший синглтон:
На самом деле, я тоже считаю, что вы не совсем правы. Я бы перефразировал это так: «Если у вас необходимость, чтобы иметь один и только один объект типа в системе, И вы необходимость, чтобы иметь глобальный доступ к нему» Я обращаю внимание на необходимость - не делайте этого, если это удобно, только если вы ДОЛЖЕН иметь это.
Ты тоже ошибаешься. Если вам нужен один и только один объект, вы создаете один и только один. Если нет логического способа разместить два экземпляра без необратимого повреждения приложения, вам следует подумать о том, чтобы сделать его синглтоном. А есть еще один аспект, глобальный доступ: если вам не нужен глобальный доступ к экземпляру, он не должен быть одноэлементным.
Я думал, что знаю шаблон синглтона из мира управляемых языков, но потом я обнаружил книгу Александреску «Современный дизайн C++: универсальное программирование и применение шаблонов проектирования». Это все изменило. Он подробно описывает все эти проблемы с Синглтоном в C++ и проектом приятеля OSS, Loki - моя первая зависимость от любого проекта C++, над которым я работаю.
«Ленивая инициализация или инициализация системы? В соответствии с вашими требованиями» .... В многопоточной системе вам придется немного усерднее работать, чтобы сделать ленивую инициализацию поточно-ориентированной; это, наверное, не стоит усилий.
Закрыто для модификации, открыто для продления. Проблема в том, что вы не можете расширить синглтон до дуплекса или триплета. Он застрял как синглтон.
Почему не следует использовать синглтон, если вы хотите сэкономить память?
@ enzom83: Синглтон с заглавной S включает код, обеспечивающий его единственность. Если вам нужен только один экземпляр, вы можете потерять этот код и просто создать один экземпляр самостоятельно ... давая вам экономию памяти одного экземпляра, плюс - экономию от отказа от кода, обеспечивающего единственность, - что также означает не жертвовать возможность создать второй экземпляр, если когда-либо ваши требования изменятся.
Не могли бы вы пояснить причину использования синглтона? Текущая формулировка сбивает с толку, потому что для меня она примерно читается как «используйте синглтон, когда вам нужно <определение синглтона>». Когда нужно <определение синглтона>? (... или это язвительный юмор?)
«Если вам нужен один-единственный объект определенного типа в системе» - «... и никогда не хотите имитировать этот объект в модульном тесте».
Я хотел бы поддержать Cygon, синглтоны быстро начинают злоупотреблять, простота доступа к ним не только увеличивает их количество как деталь реализации, но и может превратиться в экземпляр Всемогущего, способный испортить всю систему! Боль, которую они приносят, - это гораздо больше, чем польза, которую они предлагают. Внедрение зависимостей (IOC) - намного лучший способ разрешения зависимостей, которые почти всегда могут заменить потребность в синглтонах! Помните, что ваш файл заголовка должен быть WYSIWYG, не позволяйте какому-нибудь жуткому синглтону испортить вашу работу, напрямую вводя побочные эффекты где-то внутри исходного файла :-)
Я не понимаю, почему синглтон должен иметь глобальную область видимости, чтобы его можно было использовать ...
Какое отношение имеют синглтоны к потокобезопасности? Любой класс должен быть потокобезопасным только в том случае, если он должен быть потокобезопасным. Какое отношение имеет одноэлементность к безопасности потоков?
Синглтоны дают вам возможность объединить две плохие черты в одном классе. Это неправильно почти во всех смыслах.
Синглтон дает вам:
Номер один прост. Глобалы вообще плохие. Мы никогда не должны делать объекты глобально доступными, если это не нужно В самом деле.
Номер два может показаться логичным, но давайте подумаем. Когда вы в последний раз ** случайно * создавали новый объект вместо ссылки на существующий? Поскольку это помечено как C++, давайте воспользуемся примером из этого языка. Вы часто случайно пишете
std::ostream os;
os << "hello world\n";
Когда вы намеревались написать
std::cout << "hello world\n";
Конечно, нет. Нам не нужна защита от этой ошибки, потому что такой ошибки просто не бывает. Если это так, правильный ответ - пойти домой и поспать 12-20 часов в надежде, что вы почувствуете себя лучше.
Если нужен только один объект, просто создайте один экземпляр. Если один объект должен быть доступен глобально, сделайте его глобальным. Но это не значит, что невозможно создать другие его экземпляры.
Ограничение «возможен только один экземпляр» на самом деле не защищает нас от вероятных ошибок. Но это делает делает наш код очень сложным для рефакторинга и поддержки. Потому что довольно часто мы обнаруживаем потом, что нам действительно нужно более одного экземпляра. У делать более одной базы данных, у делать более одного объекта конфигурации, нам нужно несколько регистраторов. Наши модульные тесты могут захотеть иметь возможность создавать и воссоздавать эти объекты в каждом тесте, чтобы взять общий пример.
Таким образом, синглтон следует использовать тогда и только тогда, когда нам нужен обе черты, которые он предлагает: если мы необходимость глобальный доступ (что редко, потому что глобальные объекты обычно не приветствуются) и мы необходимость, чтобы предотвратить создание кем-либо из Когда-либо более одного экземпляра class (что для меня звучит как проблема дизайна). Единственная причина, по которой я это вижу, заключается в том, что создание двух экземпляров приведет к повреждению состояния нашего приложения - вероятно, потому, что класс содержит несколько статических членов или подобную глупость. В этом случае очевидный ответ - исправить этот класс. Это не должно зависеть от того, что это единственный случай.
Если вам нужен глобальный доступ к объекту, сделайте его глобальным, например std::cout. Но не ограничивайте количество создаваемых экземпляров.
Если вам абсолютно необходимо ограничить количество экземпляров класса одним, и нет никакого способа, чтобы создание второго экземпляра могло когда-либо быть безопасно обработано, тогда обеспечьте это. Но не делайте его также и глобально доступным.
Если вам действительно нужны обе черты, то 1) сделайте его одиночным и 2) дайте мне знать, для чего вам это нужно, потому что мне трудно представить себе такой случай.
Предположим, что в одном потоке вам нужен доступ к определенному объекту базы данных, который совместно используется иерархией классов ORM, классом ReportGenerator и классом DataMain maintenance. Хотя вы можете передать экземпляр базы данных в конструктор каждого объекта, это может сделать список экземпляров очень длинным и довольно неудобным для обслуживания. Вместо этого вы можете создать объект SharedDatabase как синглтон и передавать его таким образом.
или вы можете сделать его глобальным и получить только один из недостатков синглтона. С синглтоном вы одновременно ограничитесь одним экземпляром этого класса базы данных. Зачем это делать? Или вы можете посмотреть, почему у вас так много зависимостей, что список экземпляров становится «очень длинным». Все ли они необходимы? Следует ли делегировать некоторые из них другим компонентам? Возможно, некоторые из них можно было бы объединить в структуру, чтобы мы могли передавать их как один аргумент. Есть множество решений, все они лучше, чем синглтоны.
@ jalf - Вот в чем дело - Вам нужно несколько экземпляров объекта базы данных? Обычно нет, есть только одна база данных. Это один из примеров, когда полезно иметь глобальную сущность, которая может иметь только один экземпляр.
Я не знаю. Это ключ. В какой-то момент мне может понадобиться еще один экземпляр класса базы данных. Неужели так сложно представить, что приложение подключается к нескольким базам данных? Мне это не кажется таким уж странным. Но что еще более важно, зачем загонять себя в угол? Если вы подозреваете, что вам нужен только один экземпляр, создайте один экземпляр и сделайте его глобальным. Это не похоже на то, что вы «случайно» создаете новые базы данных только потому, что конструктор является общедоступным. Итак, создайте один экземпляр, сделайте его глобальным и оставьте все как есть. Нет смысла мешать мне создать еще один экземпляр, если я решу.
Возможно, я не привел хорошего примера. Один из моих коллег создал в программе с графическим интерфейсом пользователя класс под названием SpreaderController, который обеспечивал доступ к различным внутренним функциям контроллера разбрасывателя устройства контроллера разбрасывателя на муниципальном снегоочистителе. В любом снегоочистителе всегда есть и гарантированно будет только одно устройство управления разбрасывателем. Кроме того, многим различным местам в этом приложении требуется доступ к SpreaderController. У приложения тоже много разработчиков. Чтобы сохранить модель системы и предотвратить создание нескольких экземпляров другими разработчиками, он сделал ее синглтоном.
Другой пример - стойка с телефонным коммутатором. Предположим, что UK Robotics производит телефонный коммутатор с административным блоком, который управляет всеми остальными коммутационными схемами в стойке. Вам действительно нужен программно только один класс AdministrationUnit в любой момент времени на базе распределенного кода через стойку, и вам не нужно, чтобы какой-то глупый разработчик делал больше одного. Вы можете сделать его глобальным, и с точки зрения объектно-ориентированного проектирования это является преимуществом, но с точки зрения проектирования системы это фатальный недостаток - у вас не будет нескольких административных единиц в дикой природе.
Да там синглтон мог бы оправдан. Но я думаю, вы только что доказали мою точку зрения, что это необходимо только в довольно экзотических случаях. Большая часть программного обеспечения не работает с оборудованием для снегоуборочной обработки. Но я все еще не уверен. Я согласен с тем, что в вашем фактическом приложении вам нужен только один из них. Но как насчет ваших модульных тестов? Каждый из них должен работать изолированно, поэтому в идеале они должны создать свой собственный SpreaderController, что трудно сделать с синглтоном. Наконец, зачем вашим коллегам вообще нужно создавать несколько экземпляров? Это реалистичный сценарий, от которого можно защититься?
Итак, вы утверждаете, что, поскольку мы иногда взаимодействуем с физическими устройствами, из которых существует только одно, шаблон singleton хорош в целом? Я поддерживаю свое требование. Обычно это ужасная идея. Иногда, редко, и работа с внешними устройствами, такими как снегоочистители, является хорошим примером, вы действительно можете иметь только один экземпляр. Но это частные случаи. Я бы сказал, что шаблон проектирования должен быть применим не только к снегоочистителям, чтобы считаться «хорошим».
И то, что вы упустили, заключается в том, что, хотя ваши последние два примера, возможно, оправдывают ограничение «только один экземпляр», они ничего не делают, чтобы оправдать ограничение «глобально доступный». С какой стати вся кодовая база должна иметь доступ к административному устройству вашего телефонного коммутатора? Смысл синглтона - дать вам черты обе. Если вам нужен только один или другой, вам не следует использовать синглтон.
@ jalf - Моя цель состояла в том, чтобы просто показать вам пример того, где синглтон может быть полезен в дикой природе, поскольку вы даже представить себе не могли; Я полагаю, вы не часто видите, как применить это к вашей текущей работе. Я переключился на программирование снегоочистителя из бизнес-приложений исключительно потому, что это позволило мне использовать синглтон. :) j / k Я согласен с вашей предпосылкой, что есть более эффективные способы делать эти вещи, вы дали мне много поводов для размышлений. Спасибо за обсуждение!
Использование одноэлементного (АГЕМ!) «шаблона», чтобы люди не создавали больше экземпляров, просто глупо, чтобы люди не делали этого. Когда у меня есть локальная переменная foo1 типа Foo в моей небольшой функции и мне нужна только одна в функции, я не беспокоюсь о том, что кто-то собирается создать вторую переменную Foo foo2 и использовать ее вместо исходной.
Кроме того, у меня есть одноэлементный объект Math с набором методов, скажем, cos, sin, tan ... потому что "естественно" мне никогда не нужны другие способы их вычисления. Это не позволяет мне создать подкласс для настраиваемых версий этих функций (скажем, Я хочу обменять точность на скорость). Создание экземпляра исходного объекта Math, вероятно, также очень дешево, потому что большинство из них являются методами и не имеют (или очень мало) состояния.
Создание глобального просто не сокращает его, если глобальный объект должен использоваться другими глобальными объектами. Должен быть какой-то способ гарантировать, что он будет создан до того, как он будет использован, и реализация синглтона «статическая переменная функции» - хороший способ сделать это. (Ну, программа на C++ 11 могла бы сконструировать объект где-нибудь в выровненном хранилище с некоторыми глобальными переменными в заголовке, которые выполняют подсчет ссылок, но многие проекты застряли на C++ 03 или более ранней версии).
Тем не менее, вполне возможно создать статический объект функции, не делая класс одноэлементным.
@Mankarse: если ваши глобалы зависят от глобалов Другой, тогда у вас большие проблемы. Ваша программа должна выполняться внутри main, а не до него.
Когда вы работаете с другими членами команды над большим проектом (например, я работаю с видеоигрой) - вы знаете: а) люди не всегда будут знать все тонкости кода сразу или когда-либо - поэтому заставляйте / ограничение доступа - это хорошо. И б) в игровом движке, как вы хорошо знаете, вам нужен глобальный доступ к классам, если вы хотите, чтобы игра работала с разумной частотой кадров. Если вы можете создать игру без глобального доступа, которая может соответствовать скорости, простоте и экономии памяти, используя полностью нисходящий дизайн, вы будете первым (и единственным), кто сделает это. Вот почему синглтоны «модны» - потому что они работают.
@SinisterRainbow Может быть, вы могли бы объяснить старому глупому мне, как «глобальный доступ» может повлиять на вашу частоту кадров? Что касается точки "принудительного / ограничивающего доступа", я совершенно не понимаю, о чем вы говорите. Синглтон ограничивает доступ нет. Все наоборот. Это делает компонент видимым для каждый повсюду. Он добавляет зависимости, он делает код Сильнее понятным для членов команды в большой команде, которые не знают «все входы и выходы кода». Вы можете саботировать свой собственный код сколько угодно, но, пожалуйста, не притворяйтесь, что это хорошая идея.
@jalf Game Programming Gems 1, глава 1.1, страница 6, автор: Джеймс Боер (программист Arena.net, написано несколько игр, несколько книг). Вам нужны дополнительные примеры - проверьте все доступные коммерческие и бесплатные движки - попробуйте исходный код CryEngine и UDK для двух из лучших. ООП было создано для насаждения хороших привычек, но факт в том, что он работает медленнее, если вы истинно пурист в каждом случае (не говоря уже о потраченном впустую времени на попытки), и это не всегда работает, когда вам нужна скорость и гибкость. Если у вас получится лучше, возможно, вам стоит самому написать статью и сообщить об этом глупым старым Crytek и Epic Games.
@jalf Я бы привел конкретный пример, но комментарий «старый я» означает значительное эго. Вот еще один программист игр: Скотт Билас, может быть, вы слышали о нем. Тем не менее, его объяснение намного лучше моего: scottbilas.com/publications/gem-singleton
@SinisterRainbow: Нет, это означает, что я думаю, что вы ошибаетесь, и что, в конце концов, у вас есть конкретный пример нет. Не стесняйтесь обвинять в этом меня, но это не меняет фактов: ваш единственный аргумент до сих пор был апелляцией к авторитету. Вот шокирующее открытие: есть много отстойных разработчиков игр. И есть много разработчиков игр, которые пишут ненужный дрянной код без уважительной причины. Но ничто из этого не отвечает на мой вопрос: почему отсутствие синглтонов может отрицательно сказаться на частоте кадров?
Все, что вы показали, - это то, что «есть разработчики игр, использующие синглтоны», с чем я не возражаю. Но ваше утверждение заключалось в том, что использование синглтонов было необходимо для производительности, что абсурдно, потому что (а) нет затрат, связанных с нет с использованием синглтона, и (б) синглтоны означают разделяемое изменяемое состояние, которое является самой большой производительностью убийца в многопоточном коде. . А быстрые игры сегодня ... многопоточны.
Я не могу спорить с тем, что «<вставьте имя айдола> использует одиночные игры». Но я, может, выступаю против того, что «синглтоны необходимы для производительности» и «синглтоны упрощают понимание кода. Покажите мне, почему синглтоны (а) хороши и (б) необходимы. Показывая мне, что они (а) широко используются, и (б) некоторые люди считают, что они стоит использовать неуместны и не важны. Обращение к властям не является полезным способом обсуждения.
И, честно говоря, если вы прочитаете последнюю часть опубликованной вами статьи Gem, вы увидите, что он говорит о коде 15-летней давности (VC5) и что его код страдает некоторыми серьезными недостатками. Если это тот код, от которого вы хотите прислушаться к советам по дизайну, эй, не стесняйтесь. Но не удивляйтесь, если другие не согласятся с его качеством или стоимостью.
Аргументировать из-за очевидной логической ошибки апелляции к авторитету правильно. Однако вы полностью упускаете суть. Их использует каждый программист, и на то есть причина.
В чем смысл синглтонов? Во-первых, они обеспечивают концептуальную ясность, поскольку ярлыки очень важны. Вызов класса singleton и следование соглашению об именах (например, -Mgr, -Api, Global- и т. д.) Связывает важные детали о том, как мы собираемся использовать этот класс. Синглтоны также обеспечивают удобство записи. Каждый объект в системе C++ должен кому-то принадлежать. Схема владения этими объектами зависит от игры, но часто напоминает многоуровневую иерархию, где каждый более высокий уровень владеет набором дочерних объектов, каждый из которых, в свою очередь, может владеть дочерними объектами. Продолжение:
Каждый объект публикует набор функций для доступа к своим потомкам. Например, чтобы добраться до экземпляра TextureMgr, вам может потребоваться вызвать последовательность функций, таких как GetApp () -> GetServices () -> GetGui () -> GetTextureMgr (), где каждая функция возвращает указатель на запрошенный дочерний элемент. объект. Это неудобно и не совсем эффективно, учитывая множественные разыменования. Синглтоны могут решить эту проблему, поскольку они рассматриваются как глобальные объекты.
Тогда почему бы просто не использовать глобальные объекты? Они, безусловно, удобны - к объекту TextureMgr можно получить доступ через ссылку на объект g_TextureMgr, которая была объявлена с внешней связью в глобальной области (или в пространстве имен), или, возможно, через функцию, которая вместо этого возвращает ссылку на этот объект. Однако порядок построения и уничтожения глобальных объектов зависит от реализации и, как правило, невозможно предсказать переносимым способом.
Для всех этих проблем есть обходные пути, но на самом деле нам нужен способ получить удобное преимущество, заключающееся в том, чтобы рассматривать синглтон как глобальный объект без неудобств, связанных с потерей контроля над тем, когда и где он создается и уничтожается. "<< - - последние 4 сообщения взяты с сайта scottbilas.com, который вы не читали, и в дальнейшем не предоставили лучшего решения. Итак, найдите способ сэкономить несколько разыменований для каждого кадра (может быть в тысячах, и, конечно же, мы говорим о потенциальных пропусках кеша ~ = задержки в миллисекундах == затронутой частоте кадров).
- How do you implement a Singleton correctly
Есть одна проблема, о которой я никогда не упоминал, с чем я столкнулся на предыдущей работе. У нас были синглтоны C++, которые были разделены между библиотеками DLL, и обычные механизмы обеспечения единственного экземпляра класса просто не работали. Проблема в том, что каждая DLL получает свой собственный набор статических переменных вместе с EXE. Если ваша функция get_instance является встроенной или является частью статической библиотеки, каждая DLL будет иметь свою собственную копию «singleton».
Решение состоит в том, чтобы убедиться, что одноэлементный код определен только в одной DLL или EXE, или создать одноэлементный менеджер с этими свойствами для разделения экземпляров.
Йо, чувак, я слышал, вам нравятся синглтоны, поэтому я сделал синглтон для вашего синглтона, чтобы вы могли анти-паттерн, пока вы анти-паттерн.
@ Ева, да что-то в этом роде. Я не создавал проблемы, мне просто нужно было как-то заставить ее работать.
Большинство людей используют синглтоны, когда пытаются научиться использовать глобальную переменную. Существуют законные применения, но в большинстве случаев, когда люди используют их, тот факт, что может быть только один экземпляр, является просто тривиальным фактом по сравнению с тем фактом, что он доступен во всем мире.
Поскольку синглтон позволяет создать только один экземпляр, он эффективно контролирует репликацию экземпляра. например, вам не понадобится несколько экземпляров поиска - например, карта поиска Морзе, поэтому можно обернуть ее в одноэлементный класс. И то, что у вас есть единственный экземпляр класса, не означает, что вы также ограничены количеством ссылок на этот экземпляр. Вы можете ставить вызовы в очередь (чтобы избежать проблем с потоками) к экземпляру и вносить необходимые изменения. Да, общая форма синглтона является глобально общедоступной, вы, безусловно, можете изменить дизайн, чтобы создать синглтон с более ограниченным доступом. Раньше я не уставал от этого, но я уверен, что это возможно. И всем, кто прокомментировал, говоря, что шаблон синглтона является крайне злым, вы должны знать следующее: да, это зло, если вы не используете его должным образом или в пределах его эффективной функциональности и предсказуемого поведения: не ОБОБЩАЙТЕ.
Как отмечали другие, основные недостатки синглтонов включают невозможность их расширения и потерю возможности создавать более одного экземпляра, например для тестирования.
Некоторые полезные аспекты синглтонов:
Однако вам не нужно использовать синглтон, чтобы получить эти преимущества. Вы можете написать обычный объект, который выполняет эту работу, а затем предоставить людям доступ к нему через фабрику (отдельный объект). Фабрика может беспокоиться только о создании одного экземпляра, его повторном использовании и т. д., Если это необходимо. Кроме того, если вы программируете на интерфейс, а не на конкретный класс, фабрика может использовать стратегии, то есть вы можете включать и выключать различные реализации интерфейса.
Наконец, фабрика поддается использованию технологий внедрения зависимостей, таких как Spring и т. д.
Одноэлементный паттерн Мейерса большую часть времени работает достаточно хорошо, и в некоторых случаях поиск чего-то лучшего не обязательно окупается. Пока конструктор никогда не будет бросать и между синглтонами нет зависимостей.
Синглтон - это реализация глобально доступный объект (с этого момента GAO), хотя не все GAO являются одиночными.
Сами регистраторы не должны быть одиночными, но в идеале средства ведения журнала должны быть глобально доступными, чтобы отделить, где создается сообщение журнала, откуда и как оно регистрируется.
Ленивая загрузка / ленивая оценка - это другая концепция, и синглтон обычно реализует ее. У него есть множество собственных проблем, в частности безопасность потоков и проблемы, если он не работает с исключениями, так что то, что казалось хорошей идеей в то время, в конце концов, оказалось не так уж и хорошо. (Немного похоже на реализацию COW в строках).
Имея это в виду, GOA можно инициализировать следующим образом:
namespace {
T1 * pt1 = NULL;
T2 * pt2 = NULL;
T3 * pt3 = NULL;
T4 * pt4 = NULL;
}
int main( int argc, char* argv[])
{
T1 t1(args1);
T2 t2(args2);
T3 t3(args3);
T4 t4(args4);
pt1 = &t1;
pt2 = &t2;
pt3 = &t3;
pt4 = &t4;
dostuff();
}
T1& getT1()
{
return *pt1;
}
T2& getT2()
{
return *pt2;
}
T3& getT3()
{
return *pt3;
}
T4& getT4()
{
return *pt4;
}
Необязательно делать это так грубо и ясно, что в загруженной библиотеке, содержащей объекты, вам, вероятно, понадобится какой-то другой механизм для управления их временем жизни. (Поместите их в объект, который вы получите при загрузке библиотеки).
А когда я использую синглтоны? Я использовал их для 2 вещей - Одноэлементная таблица, указывающая, какие библиотеки были загружены с помощью dlopen. - Обработчик сообщений, на который могут подписаться регистраторы и которому вы можете отправлять сообщения. Требуется специально для обработчиков сигналов.
Другая реализация
class Singleton
{
public:
static Singleton& Instance()
{
// lazy initialize
if (instance_ == NULL) instance_ = new Singleton();
return *instance_;
}
private:
Singleton() {};
static Singleton *instance_;
};
Это действительно ужасно. См .: stackoverflow.com/questions/1008019/c-singleton-design-patte rn /… для лучшей ленивой инициализации и, что более важно, гарантированного детерминированного уничтожения.
Если вы собираетесь использовать указатели, Instance() должен возвращать указатель, а не ссылку. Внутри файла .cpp инициализируйте экземпляр нулевым значением: Singleton* Singleton::instance_ = nullptr;. А Instance() должен быть реализован как if (instance_ == nullptr) instance_ = new Singleton(); return instance_;.
Я до сих пор не понимаю, почему синглтон должен быть глобальным.
Я собирался создать синглтон, в котором я спрятал базу данных внутри класса как частную постоянную статическую переменную и создал функции класса, которые используют базу данных, не открывая базу данных пользователю.
Я не понимаю, почему эта функциональность будет плохой.
Я не понимаю, почему вы думаете, что он должен быть глобальным.
согласно этой теме, все говорили, что синглтон должен быть глобальным
Нет. Поток указывает, что одиночный элемент имеет глобальное состояние. Не то чтобы это глобальная переменная. Предлагаемое вами решение имеет глобальное состояние. Предлагаемое вами решение также использует глобальную переменную; статический член класса - это объект «Длительность статического хранения», а глобальная переменная - это объект «Длительность статического хранения». Таким образом, это в основном одно и то же, но с немного разной семантикой / областью действия.
Значит, частная статическая переменная по-прежнему является глобальной из-за «Длительности статического хранения»?
Его область действия ограничена классом. Но вы можете получить к нему доступ из любой части приложения. Разве это не звучит глобально? Но термин «глобальная переменная» бессмысленен с точки зрения стандарта. Есть типы объектов четыре в C++ «Длительность статического хранения», «Продолжительность автоматического хранения», «Продолжительность динамического хранения» и «Продолжительность хранения потока». То, что вы называете глобальными и статическими членами, попадает в первую категорию (единственная разница - это область, в которой они видны). Кроме того, в чем разница.
Примечание: вы пропустили мою намеренно не оговоренную часть. Ваш дизайн использования статического «частного» члена неплох, как и одиночный. Потому что он не вводит «глобальное изменяемое состояние». Но это тоже не синглтон. Синглтон - это класс, который разработан так, что может существовать только один экземпляр объекта. Вы предлагаете единое общее состояние для всех объектов класса. Другая концепция.
Если вам нужен обзор дизайна. Приходите и посетите codereview.stackexchange.com
Благодаря тонну. Я начинаю понимать. Поэтому в моем случае, даже если мне нужен только один экземпляр класса, поскольку все методы, вероятно, в конечном итоге станут статическими, синглтон, вероятно, не лучший выбор. Есть ли у вас какие-либо предложения по шаблону дизайна, который я должен использовать для этого?
Я считаю их полезными, когда у меня есть класс, который инкапсулирует много памяти. Например, в недавней игре, над которой я работал, у меня есть класс карты влияния, который содержит набор очень больших массивов непрерывной памяти. Я хочу, чтобы все это было выделено при запуске, все освобождено при завершении работы, и мне определенно нужна только одна его копия. Я также должен получить к нему доступ из многих мест. Я считаю, что в этом случае очень полезен шаблон singleton.
Я уверен, что есть и другие решения, но я считаю это очень полезным и простым в реализации.
Если вы являетесь тем, кто создал синглтон и кто его использует, не делайте его синглтоном (это не имеет смысла, потому что вы можете контролировать сингулярность объекта, не делая его синглтоном), но это имеет смысл, когда вы разработчик библиотека, и вы хотите предоставить своим пользователям только один объект (в этом случае вы являетесь тем, кто создал синглтон, но не являетесь пользователем).
Синглтоны - это объекты, поэтому используйте их как объекты, многие люди обращаются к синглетонам напрямую, вызывая метод, который их возвращает, но это вредно, потому что вы заставляете свой код знать, что объект является синглтоном, я предпочитаю использовать синглтоны как объекты, я передаю их через конструктор, и я использую их как обычные объекты, таким образом, ваш код не знает, являются ли эти объекты одиночными или нет, и это делает зависимости более понятными, и это немного помогает для рефакторинга ...
Ниже представлен лучший подход для реализации поточно-безопасного одноэлементного шаблона с освобождением памяти в самом деструкторе. Но я думаю, что деструктор должен быть необязательным, потому что экземпляр синглтона будет автоматически уничтожен при завершении программы:
#include<iostream>
#include<mutex>
using namespace std;
std::mutex mtx;
class MySingleton{
private:
static MySingleton * singletonInstance;
MySingleton();
~MySingleton();
public:
static MySingleton* GetInstance();
MySingleton(const MySingleton&) = delete;
const MySingleton& operator=(const MySingleton&) = delete;
MySingleton(MySingleton&& other) noexcept = delete;
MySingleton& operator=(MySingleton&& other) noexcept = delete;
};
MySingleton* MySingleton::singletonInstance = nullptr;
MySingleton::MySingleton(){ };
MySingleton::~MySingleton(){
delete singletonInstance;
};
MySingleton* MySingleton::GetInstance(){
if (singletonInstance == NULL){
std::lock_guard<std::mutex> lock(mtx);
if (singletonInstance == NULL)
singletonInstance = new MySingleton();
}
return singletonInstance;
}
Что касается ситуаций, когда нам нужно использовать одноэлементные классы, можно: Если мы хотим поддерживать состояние экземпляра на протяжении всего выполнения программы Если мы занимаемся записью в журнал выполнения приложения, где нужно использовать только один экземпляр файла ... и так далее. Будет заметно, если кто-нибудь предложит оптимизацию в моем приведенном выше коде.
Это точно не лучше. 1: Вы не определяете семантику владения с помощью указателя. Никогда не используйте указатели в C++, если вы не готовы ими управлять. 2: Ваше использование блокировки с двойной проверкой устарело, и есть намного лучшие современные способы сделать это. 3: Ваши комментарии по поводу разрушения наивны. Освобождение памяти - это не вопрос использования деструктора, это очистка. Предложения по улучшению версии: посмотрите на вопрос. Представленная там версия уже намного лучше.
Что, если позже вы решите, что хотите использовать несколько регистраторов? Или несколько пулов потоков? Если вам нужен только один регистратор, создайте только один экземпляр и сделайте его глобальным. Синглтоны хороши только в том случае, если вам абсолютно НУЖНО, чтобы там был только один, и он НЕОБХОДИМ быть глобальным, ИМХО.