Подтвердить что ты не робот

С++ singleton vs полностью статический объект

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

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

1) Классический шаблон Singleton

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


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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

Ответ 2

Другой вариант, который вы упускаете, - namespace.

namespace xyz {
namespace {
    int private_variable;
}

int get_pv() {
    return private_variable;
}
}

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

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

int x = xyz::get_pv();

Ответ 3

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 4

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

Ответ 5

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