C ++ singleton против полностью статического объекта - PullRequest
33 голосов
/ 01 октября 2010

Допустим, нам нужен только один экземпляр некоторого класса в нашем проекте. Есть несколько способов сделать это.

Я хочу сравнить. Пожалуйста, вы можете просмотреть мое понимание.

1) Классическая модель Singleton

2) Полностью статический класс (все методы и члены являются статическими). ​​


Как я понимаю различия следующие:

a) Порядок инициализации статических элементов в разных единицах не определен. Таким образом, полностью статическая инициализация членов не может использовать статические члены / функции из других модулей. И синглтон не имеет этой проблемы.

б) Мы должны иметь дело с многопоточностью для getInstance () Singleton. Однако у полностью статического класса такой проблемы нет.

в) Доступ к методам выглядит немного иначе. Foo :: бар (); vs Foo :: getInstance () -> bar (); Как правило, singleton может возвращать NULL, чтобы определить, что были некоторые проблемы с конструкцией объекта, а статический класс не может.

d) Определение класса выглядит немного неуклюжим с кучей статик для статического класса.

Я что-то пропустил?

Ответы [ 5 ]

20 голосов
/ 01 октября 2010

Называете ли вы это Singleton или Monostate или какое-либо причудливое имя ... очень раздражает то, что у вас есть ОДИН экземпляр объекта и многие пишут в него: глобальные переменные, независимо от их вида, являются злом.

Идея, что вам нужен уникальный экземпляр, обычно неуклюжа.В большинстве случаев вам действительно нужны части, которые обмениваются данными с одним и тем же экземпляром.Но другая группа частей может без проблем использовать другой экземпляр.

Любой код, который утверждает, что нуждается в глобальной переменной, вызывает большие подозрения.Это может показаться проще, но давайте посмотрим правде в глаза, вы могли бы идеально передать объект каждой функции, это усложнило бы их сигнатуру, но это все равно сработало бы.

Однако, я признаю, это кажется прощеиспользовать глобальные переменные ... пока вы не заметите проблемы:

  • многопоточность скомпрометирована
  • тестируемость сокращена, так как один тестможет повлиять на следующий за ним
  • анализ зависимостей чрезвычайно сложен: трудно понять, от какого состояния зависит ваш метод, когда вы извлекаете глобальные изнутри подметоды ...

Теперь,Что касается синглтона, то многопоточное создание не может быть использовано в C ++ до C ++ 0x (когда это становится возможным с помощью статических локалей), поэтому вам нужно создать его только в одном потоке и отложить доступ перед: создать его в main, этоВаша лучшая ставка.

Разрушение может привести к хаосу, поскольку жизнь Синглтона / Статика может закончиться, прежде чем другие покончат сэто, а затем это неопределенное поведение.Это типично для Logger синглтона.Обычная стратегия - бесстыдно просачиваться ...

После этого, если вы все еще хотите его, я желаю вам удачи, вот и все, что может сделать это сообщество для вас.

10 голосов
/ 01 октября 2010

Другая опция, которую вы пропускаете, это namespace.

namespace xyz {
namespace {
    int private_variable;
}

int get_pv() {
    return private_variable;
}
}

Функционально, это будет похоже на ваш вариант # 2, но вы не можете случайно «удалить» эту вещь.Вы не можете случайно создать экземпляр этого.Это просто набор связанных глобально доступных данных и функций.Вы можете (как в моем примере) даже иметь «закрытые» члены и функции.

Конечно, использование будет примерно таким:

int x = xyz::get_pv();
1 голос
/ 01 октября 2010

Вы также можете использовать шаблон Borg, который немного сложнее в C ++, если у вас нет доступа к общему классу указателей.Идея состоит в том, что может быть создано любое количество классов Борг, но их состояние является общим для всех экземпляров.

1 голос
/ 01 октября 2010

Допустим, нам нужен только один экземпляр некоторого класса в нашем проекте.Есть несколько способов сделать это.

Лучшее решение:

Переменная в main, которую вы передаете в качестве параметра всем необходимым функциям, будет другой.

a) Порядок инициализации статических элементов в разных единицах не определен.Таким образом, полностью статическая инициализация членов не может использовать статические члены / функции из других модулей.И синглтон не имеет этой проблемы.

Синглтоны имеют эту проблему, если их конструктор / деструктор обращается к другой глобальной статической переменной продолжительности жизни.

b) Нам нужно разобратьсяс потоками для getInstance () Sigleton.Однако в полностью статическом классе такой проблемы нет.

Не правда ли, проблема?Если вам это известно, просто добавьте соответствующие блокировки в код.

c) Доступ к методам выглядит немного иначе.Foo :: бар ();vs Foo :: getInstance () -> bar ();Как правило, sigleton может возвращать NULL, чтобы идентифицировать, что были некоторые проблемы с конструкцией объекта, а статический класс не может.

Я бы заставил свой getInstance () возвращать ссылку.Тогда нет никакой неоднозначности, если указатель равен NULL.Это или сработало или бросило исключение.Также это приводит к созданию схемы, в которой уничтожение корректно вызывается для экземпляра (не принимайте это как совет по использованию Singleton, я бы избегал его, если это возможно (но если вы его используете, сделайте его аккуратным)).

d) Определение класса выглядит немного неуклюжим с кучей статики для статического класса.

Нет более сложного, чем правильная запись синглтона.

Проблема с обоими этими методамиявляется то, что они оба обращаются к global mutable state, и, следовательно, использование этих объектов «одного экземпляра» другими объектами скрыто от пользователя.Это может привести к проблемам с тестированием (TDD требует возможности имитировать внешнюю функциональность, но global mutable state не позволяет тестировщику имитировать внешние зависимости (легко)).

Любой объект, не являющийся POD, имеет конструкторэто может потенциально вызвать исключение.Таким образом, для объектов в глобальном пространстве имен это означает, что исключения могут быть сгенерированы до того, как будет введена функция main () (это может привести к трудностям при поиске ошибок (если у вас много глобальных объектов (вы должны ставить точки останова везде)). Нота же проблема существует с синглтоном, который лениво оценивается: если при первом использовании он выдает, как исправить это, чтобы при последующей попытке не сработал? Оу, будет ли ваше приложение продолжать генерировать каждый раз, когда извлекается синглтон?

0 голосов
/ 01 октября 2010

Вы можете добавить: статические объекты могут генерировать исключения.Исполняемый файл не запускается, и его трудно отлаживать / обрабатывать хорошо.

...