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

Разница между реализацией Singleton с использованием указателя и использованием статического объекта

EDIT: Извините, мой вопрос не ясен, почему книги/статьи предпочитают реализацию # 1 над реализацией # 2?

Каково фактическое преимущество использования указателя в реализации класса Singleton vs с использованием статического объекта? Почему большинство книг предпочитают это

class Singleton
{
  private:

    static Singleton *p_inst;
    Singleton();

  public:

    static Singleton * instance()
    {
      if (!p_inst)
      {
        p_inst = new Singleton();
      }

      return p_inst;
    }
};

над этим

class Singleton
{
  public:
    static Singleton& Instance()
    {
        static Singleton inst;
        return inst;
    }

  protected:
    Singleton(); // Prevent construction
    Singleton(const Singleton&); // Prevent construction by copying
    Singleton& operator=(const Singleton&); // Prevent assignment
    ~Singleton(); // Prevent unwanted destruction
};
4b9b3361

Ответ 1

почему книги/статьи предпочитают реализацию # 1 над реализацией # 2?

Поскольку большинство статей, описывающих анти-шаблон Singleton, не полностью понимают все скрытые опасности при попытке реализовать его безопасно на С++. На удивление трудно понять это.

Каково фактическое преимущество использования указателя при реализации класса Singleton vs с использованием статического объекта?

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

Используя локальный статический объект, создание является потокобезопасным для любого компилятора, который поддерживает модель потоковой передачи С++ 11; Кроме того, объект будет уничтожен в конце программы. Однако можно получить доступ к объекту после его уничтожения (например, от деструктора другого статического объекта), что может привести к неприятным ошибкам.

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

Ответ 2

Вторая версия (с использованием локальной статической переменной) имеет значительные преимущества.

Он не требует использования свободного хранилища, и поэтому не будет обнаружен как утечка памяти. Он потокобезопасен (в C++11). Он короче и проще.

Единственными минусами являются невозможность сделать его portably threadsafe (для компиляторов pre-С++ 11) и что он не дает вам возможности явно уничтожить экземпляр singleton.

Ответ 3

Я бы всегда предпочитал второй, но первый имеет пару потенциально интересных преимуществ: -

  • Ясность - проверка нулевого указателя эффективно что делает компилятор под капотом при построении статического объекты. С точки зрения "обучения" это поучительно понять, что происходит при использовании статических объектов в методе сфера.

  • Lazy Allocation - в первом случае объект Singleton кучного выделено. Если ваша функция никогда не запускается, объект никогда не будет сконструировал и никогда не потребляет память. Но, во втором случае, память назначается компоновщиком для хранения объекта до запускается программа, хотя "конструкция" ленива.

Ответ 4

Второй имеет недетерминированное разрушение. Первый, вы контролируете, когда вы удаляете указатель, если это вообще возможно.

Первая конструкция, конечно, не является потокобезопасной, но ее можно сделать с помощью boost::call_once (или std::call_once, если она доступна)

Вторая конструкция была достаточно распространена, что многие компиляторы сделали ее потокобезопасной, даже если технически по стандарту это не так (хотя по стандарту объект должен быть создан только один раз, я не уверен в стандартном представлении на завершение строительства до использования другой нити).

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

Ответ 5

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

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

Ответ 6

Второй пример известен под именем "Meyers 'Singleton", потому что он был опубликован сначала в "Эффективном С++" или "Более эффективный С++". Я не уверен, какой из них, но оба они были опубликованы после "Шаблонов дизайна", поэтому "Банда четырех" точно так же не знала о втором шаблоне, когда была написана их книга.

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

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

Ответ 7

Как насчет нелокальной статики? Кто-нибудь видит проблему с этим?

class Singleton
{
    static Singleton singleton;
    Singleton();
    // etc

public:
    static Singleton &Instance() { return singleton; }
};

Singleton Singleton::singleton;

// etc