W jaki sposób implementacja Singletonu jest w rzeczywistości Singletonem
Czytałem dużo o Singletonach, kiedy powinny i nie powinny być używane, i jak je bezpiecznie wdrożyć. Piszę w C++11 i natknąłem się na leniwą inicjalizowaną implementację Singletona, co widać w tym pytaniu.
Ta implementacja to:
static Singleton& instance()
{
static Singleton s;
return s;
}
Rozumiem, że to wątek bezpieczny od innych pytań tutaj na SO, ale nie rozumiem, jak to jest w rzeczywistości wzór Singletona. Mam zaimplementowane singletony w innych językach, a te zawsze kończą się czymś takim jak ten przykład z Wikipedii :
public class SingletonDemo {
private static volatile SingletonDemo instance = null;
private SingletonDemo() { }
public static SingletonDemo getInstance() {
if (instance == null) {
synchronized (SingletonDemo .class){
if (instance == null) {
instance = new SingletonDemo ();
}
}
}
return instance;
}
}
Kiedy patrzę na ten drugi przykład, jest bardzo intuicyjne, jak to jest singleton, ponieważ klasa posiada odniesienie do jednej instancji samej w sobie i tylko zawsze zwraca tę instancję. Jednak w pierwszym przykładzie nie rozumiem, jak to uniemożliwia istnienie dwóch instancji obiektu. Więc moje pytania to:
- jak pierwszy implementacja wzorca Singletona? Zakładam, że ma to związek ze statycznym słowem kluczowym, ale mam nadzieję, że ktoś wyjaśni mi dogłębnie, co dzieje się pod maską.
- pomiędzy tymi dwoma stylami implementacji, czy jeden jest lepszy od drugiego? Jakie są plusy i minusy?
Dzięki za pomoc,
2 answers
Jest to singleton, ponieważ static
czas przechowywania funkcji local oznacza, że w programie istnieje tylko jedna instancja tej lokalnej.
static bool __guard = false;
static char __storage[sizeof(Singleton)]; // also align it
Singleton& Instance() {
if (!__guard ) {
__guard = true;
new (__storage) Singleton();
}
return *reinterpret_cast<Singleton*>(__storage);
}
// called automatically when the process exits
void __destruct() {
if (__guard)
reinterpret_cast<Singleton*>(__storage)->~Singleton();
}
Bity zabezpieczające wątek sprawiają, że staje się to nieco bardziej skomplikowane, ale zasadniczo jest to to samo.
Patrząc na rzeczywistą implementację dla C++11, jest zmienna strażnicza dla każdej statycznej (jak powyższa logika), która jest również używana dla barier i wątków. Spójrz na wyjście AMD64 clanga dla:
Singleton& instance() {
static Singleton instance;
return instance;
}
Montaż AMD64 dla instance
z Ubuntu Clang 3.0 na AMD64 at-O1 (dzięki uprzejmości http://gcc.godbolt.org / jest:
instance(): # @instance()
pushq %rbp
movq %rsp, %rbp
movb guard variable for instance()::instance(%rip), %al
testb %al, %al
jne .LBB0_3
movl guard variable for instance()::instance, %edi
callq __cxa_guard_acquire
testl %eax, %eax
je .LBB0_3
movl instance()::instance, %edi
callq Singleton::Singleton()
movl guard variable for instance()::instance, %edi
callq __cxa_guard_release
.LBB0_3:
movl instance()::instance, %eax
popq %rbp
ret
Widać, że odwołuje się do straży globalnej, aby sprawdzić, czy inicjalizacja jest wymagana, używa __cxa_guard_acquire
, testuje inicjalizację ponownie, i tak dalej. Dokładnie pod każdym względem jak w wersji, którą zamieściłeś z wikipedii, z wyjątkiem użycia zestawu AMD64 i symboli / układu określonego w Itanium ABI.
Zauważ, że jeśli uruchomisz ten test, powinieneś dać Singleton
nietrywialny konstruktor, więc nie jest to POD, w przeciwnym razie optymalizator zda sobie sprawę, że nie ma sensu wykonywać całej tej pracy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-09 05:54:59
// Singleton.hpp
class Singleton {
public:
static Singleton& Instance() {
static Singleton S;
return S;
}
private:
Singleton();
~Singleton();
};
Ta implementacja jest znana jako Singleton Meyersa. Scott Meyers says:
" to podejście opiera się na gwarancji C++, że lokalne obiekty statyczne są inicjowane, gdy po raz pierwszy zostanie napotkana definicja obiektu podczas wywołania tej funkcji." ... "Jako bonus, jeśli nigdy nie zadzwonisz do funkcja emulująca nielokalny obiekt statyczny, nigdy nie ponosisz kosztów budowy i niszczenia obiektu."
When you call
Singleton& s=Singleton::Instance()
pierwszy raz obiekt jest utworzone i każde następne wywołanie Singleton::Instance()
powoduje zwrócenie tego samego obiektu.
Główne wydanie:
- z zastrzeżeniem fiasko zlecenia zniszczenia (odpowiednik fiasko zlecenia inicjalizacji)
Kolejna implementacja nazywa się wierny nieszczelny Singleton.
class Singleton {
public:
static Singleton& Instance() {
if (I == nullptr) { I = new Singleton(); }
return *I;
}
private:
Singleton();
~Singleton();
static Singleton* I;
};
// Singleton.cpp
Singleton* Singleton::I = 0;
Dwa zagadnienia:
- przecieki, chyba że zaimplementujesz Release i upewnij się, że zadzwonisz (raz)
- nie thread safe
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-18 20:43:07