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:

  1. 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ą.
  2. pomiędzy tymi dwoma stylami implementacji, czy jeden jest lepszy od drugiego? Jakie są plusy i minusy?

Dzięki za pomoc,

Author: Community, 2013-07-17

2 answers

Jest to singleton, ponieważ static czas przechowywania funkcji local oznacza, że w programie istnieje tylko jedna instancja tej lokalnej.

W przeciwieństwie do C++98, C ++ 98 może być zaimplementowany w C ++ 98, ale nie może być zaimplementowany w C ++ 98.]}
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.

 49
Author: Sean Middleditch,
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:


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
 19
Author: 4pie0,
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