Szukaj…


Uwagi

Singleton został zaprojektowany, aby zapewnić, że klasa ma tylko jedną instancję i zapewnia globalny punkt dostępu do niej. Jeśli potrzebujesz tylko jednego wystąpienia lub wygodnego globalnego punktu dostępu, ale nie obu, rozważ inne opcje przed przejściem do singletonu.

Zmienne globalne mogą utrudniać rozumowanie kodu. Na przykład, jeśli jedna z funkcji wywołujących nie jest zadowolona z danych otrzymywanych od Singletona, musisz teraz prześledzić, co najpierw daje singletonowi złe dane.

Singletony również zachęcają do sprzęgania , terminu używanego do opisania dwóch składników kodu, które są ze sobą połączone, zmniejszając w ten sposób własną miarę samowystarczalności każdego elementu.

Singletony nie są przyjazne dla współbieżności. Gdy klasa ma globalny punkt dostępu, każdy wątek ma do niej dostęp, co może prowadzić do impasu i warunków wyścigu.

Wreszcie opóźniona inicjalizacja może powodować problemy z wydajnością, jeśli zostanie zainicjowana w niewłaściwym czasie. Usunięcie leniwej inicjalizacji usuwa również niektóre funkcje, które sprawiają, że Singleton jest interesujący w pierwszej kolejności, takie jak polimorfizm (patrz Podklasy).

Źródła: wzorce programowania gier autorstwa Roberta Nystrom

Leniwa inicjalizacja

Ten przykład został przeniesiony z sekcji Q & A tutaj: http://stackoverflow.com/a/1008289/3807729

Zobacz ten artykuł, aby zapoznać się z prostym projektem leniwego ocenianego z gwarantowanym singletonem zniszczenia:
Czy ktoś może dostarczyć mi próbkę Singletona w c ++?

Klasyczny leniwy ocenił i poprawnie zniszczył singletona.

class S
{
    public:
        static S& getInstance()
        {
            static S    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        S() {};                   // Constructor? (the {} brackets) are needed here.

        // C++ 03
        // ========
        // Dont forget to declare these two. You want to make sure they
        // are unacceptable otherwise you may accidentally get copies of
        // your singleton appearing.
        S(S const&);              // Don't Implement
        void operator=(S const&); // Don't implement

        // C++ 11
        // =======
        // We can use the better technique of deleting the methods
        // we don't want.
    public:
        S(S const&)               = delete;
        void operator=(S const&)  = delete;

        // Note: Scott Meyers mentions in his Effective Modern
        //       C++ book, that deleted functions should generally
        //       be public as it results in better error messages
        //       due to the compilers behavior to check accessibility
        //       before deleted status
};

Przeczytaj ten artykuł o tym, kiedy używać singletona: (nie często)
Singleton: Jak go używać

Zobacz dwa artykuły na temat kolejności inicjalizacji i radzenia sobie z nią:
Kolejność inicjalizacji zmiennych statycznych
Znalezienie problemów z kolejnością statycznej inicjalizacji C ++

Zobacz ten artykuł opisujący czasy życia:
Jaki jest czas życia zmiennej statycznej w funkcji C ++?

Zobacz ten artykuł, który omawia niektóre implikacje wątków dla singletonów:
Instancja Singleton zadeklarowana jako zmienna statyczna metody GetInstance

Zobacz ten artykuł, który wyjaśnia, dlaczego podwójne sprawdzanie blokowania nie działa w C ++:
Jakie są wszystkie typowe niezdefiniowane zachowania, o których powinien wiedzieć programista C ++?

Podklasy

class API
{
public:
    static API& instance();
    
    virtual ~API() {}
    
    virtual const char* func1() = 0;
    virtual void func2() = 0;
    
protected:
    API() {}
    API(const API&) = delete;
    API& operator=(const API&) = delete;
};

class WindowsAPI : public API
{
public:
    virtual const char* func1()  override { /* Windows code */ }
    virtual void func2() override { /* Windows code */ }    
};

class LinuxAPI : public API
{
public:
    virtual const char* func1() override { /* Linux code */ }
    virtual void func2() override { /* Linux code */ }    
};

API& API::instance() {
#if PLATFORM == WIN32
    static WindowsAPI instance;
#elif PLATFORM = LINUX
    static LinuxAPI instance;
#endif
    return instance;
}

W tym przykładzie prosty przełącznik kompilatora wiąże klasę API z odpowiednią podklasą. W ten sposób można uzyskać dostęp do API bez konieczności łączenia się z kodem specyficznym dla platformy.

Singeton bezpieczny dla wątków

C ++ 11

Standardy C ++ 11 gwarantują, że inicjalizacja obiektów zakresu funkcji jest inicjowana w sposób zsynchronizowany. Można to wykorzystać do wdrożenia bezpiecznego dla wątków singletona z leniwą inicjalizacją .

class Foo
{
public:
    static Foo& instance()
    {
        static Foo inst;
        return inst;
    }        
private:
    Foo() {}
    Foo(const Foo&) = delete;
    Foo& operator =(const Foo&) = delete;
};

Statyczny, bezpieczny dla deinicjalizacji singleton.

Są chwile z wieloma obiektami statycznymi, w których musisz być w stanie zagwarantować, że singleton nie zostanie zniszczony, dopóki wszystkie statyczne obiekty korzystające z singletonu nie będą go już potrzebować.

W tym przypadku std::shared_ptr może być użyte do utrzymania singletona przy życiu dla wszystkich użytkowników, nawet gdy statyczne destrukatory są wywoływane na końcu programu:

class Singleton
{
public:
    Singleton(Singleton const&) = delete;
    Singleton& operator=(Singleton const&) = delete;

    static std::shared_ptr<Singleton> instance()
    {
        static std::shared_ptr<Singleton> s{new Singleton};
        return s;
    }

private:
    Singleton() {}
};

UWAGA: Ten przykład pojawia się jako odpowiedź w sekcji Pytania i odpowiedzi tutaj.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow