Действительно ли возможна ленивая инициализация со статическими членами данных? - PullRequest
3 голосов
/ 16 августа 2011

Можно ли инициализировать экземпляр Singleton, когда он действительно необходим?

Рассмотрим этот шаблон, взятый из знаменитых «шаблонов проектирования»:

class Singleton {
public:
   static Singleton* Instance();
protected:
   Singleton();
private:
   static Singleton* _instance;
}

Singleton* Singleton::_instance = 0; // unit.cpp

static Singleton* Singleton::Instance() {
   if (_instance == 0) {
       _instance = new Singleton;
   }
   return _instance;
}

Теперь, я думаю,в шаблоне есть проблема, если кто-то хочет предоставить Singleton в библиотеке другим: если пользователь вызывает Singleton::Instance() из другого модуля компиляции (например, во время инициализации элемента статических данных) до инициализации _instance, тогдаследующий вызов Singleton::Instance() может создать другой экземпляр Singleton с нежелательными результатами, так как _instance мог бы быть инициализирован сначала до 0.

Я думаю, что одним из решений является инициализация _instance следующим образом:

Singleton* Singleton::_instance = Singleton::Instance();

В любом случае, это делает инициализацию не «ленивой» для тех, кому не нужно вызывать Singleton :: Instance () для инициализации своих статических данных.

Существуют ли более эффективные решения для инициализацииможет произойти, когда нужен экземпляр Singleton?

Ответы [ 3 ]

7 голосов
/ 16 августа 2011

Статическая инициализация происходит перед динамической инициализацией.

Одноэлементное решение, которое вы процитировали, работает, потому что

Singleton* Singleton::_instance = 0;

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

Другое предоставленное вами решение не меняет ничего существенного, потому что все жегарантированно произойдет только нулевая инициализация _instance до вызова кода из других модулей, так как инициализация с помощью вызова Singleton :: Instance является динамической и поэтому подчиняется static фиаско порядка инициализации .

Реализация - сегмент данных

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

Терминология, стандартная кавычка

В то время как большинство программистов (включая Bjarne Stroustrup ) вызывают стиль инициализации, использованный в оригинальной одноэлементной реализации "инициализация времени компиляции"стандарт называет это "статic инициализация », в отличие от« динамической инициализации »(которая чаще всего называется динамической инициализацией).См. C ++ 0x черновик 3.6.2 (укороченный, выделенный):

3.6.2 Инициализация нелокальных переменных [basic.start.init]

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

2 Переменные со статической продолжительностью хранения (3.7.1) ... должны быть инициализированы нулями (8.5) перед любой другой инициализацией.

Инициализация константы выполняется: ...

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

Вместе, нулевая инициализация и постоянная инициализация называются статической инициализацией;все остальные инициализации - это динамическая инициализация.Статическая инициализация должна быть выполнена до любой динамической инициализации.

4 голосов
/ 16 августа 2011

Теперь, я думаю, в паттерне есть проблема, если кто-то хочет предоставить Singleton в библиотеке другим: если пользователь вызывает Singleton :: Instance () из другого модуля компиляции (во время статическогоИнициализация элемента данных, например) до инициализации _instance, затем следующий вызов Singleton :: Instance () может создать другой экземпляр Singleton с нежелательными результатами, так как _instance мог быть инициализирован в 0 первым.

Нет проблем: статические переменные инициализируются нулями перед любой динамической инициализацией.

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

Singleton* Singleton::_instance = Singleton::Instance();

проблематично - нет гарантии порядка (динамической) инициализации между единицами перевода.

Приветствия & hth.,

2 голосов
/ 16 августа 2011

использовать локальную статику

Singleton* Singleton::getInstance()
{
    static Singleton obj; return &Singleton;
}
...