Далее будет использован тот факт, что инициализация до вызова main()
будет выполняться в одном потоке, и указатель pInstance
должен быть инициализирован нулем, прежде чем произойдет динамическая инициализация статических объектов (3.6.2«Инициализация нелокальных объектов»).
class A {
public:
A(const char* s) { /*...*/ };
static const A& get_default() {
if (!pInstance) {
static const A default_instance("default");
pInstance = &default_instance;
}
return *pInstance;
}
private:
static A const* pInstance;
};
A const* A::pInstance = &A::get_default();
Таким образом, pInstance
будет иметь значение NULL при первом вызове get_default()
- независимо от того, вызван ли этот вызов инициализацией A::pInstance
.Это заставит get_default()
инициализировать его (pInstance
будет «без необходимости» перезаписываться тем же значением при запуске инициализатора - но это будет в одном потоке инициализации, поэтому проблем с потоками не будет).И чтобы было ясно, инициализатор для pInstance
заставит get_default()
вызываться в потоке инициализации, даже если ничего не происходит.
Последующие вызовы get_default()
не изменят pInstance
, поэтомуэто потокобезопасно.
Обратите внимание, что проще:
class A {
public:
A(const char* s) { /*...*/ };
static const A& get_default() {
static const A& a = A("default"); /* ..but not thread-safe */
return a;
}
};
namespace {
A const& trigger = A::get_default();
}
По той же причине должно быть также потокобезопасно, но у него есть пара областей, которые делают меня немного менее уверенным, что он не будетимеют некоторые потенциальные недостатки:
- , если набор инструментов определяет, что
trigger
не используется в другом месте (и это применимо, даже если мы вытащим его из анонимного пространства имен), я бы опасался, что это можетудалить из образа программы.Я не уверен в том, что может дать стандарт по предотвращению этой оптимизации, особенно если class A
живет в библиотеке. - предположительно, компилятор просто проверяет скрытый статический флаг, чтобы определить, инициализирована ли локальная
static
переменная в get_default()
была запущена ранее.Однако на самом деле нет никаких обещаний относительно того, какой механизм используется (я не изучал, что C ++ 0x может сказать по этому поводу).В первом примере потокобезопасная проверка после вызова init get_default()
прямо здесь - гарантировано.