C ++ Singleton wzorzec projektowy


735

Ostatnio natknąłem się na realizację / implementację wzorca projektowego Singleton dla C ++. Wyglądało to tak (przyjąłem z prawdziwego przykładu):

// a lot of methods are omitted here
class Singleton
{
   public:
       static Singleton* getInstance( );
       ~Singleton( );
   private:
       Singleton( );
       static Singleton* instance;
};

Z tej deklaracji mogę wywnioskować, że pole instancji jest inicjowane na stercie. Oznacza to, że istnieje przydział pamięci. Co jest dla mnie całkowicie niejasne, kiedy dokładnie pamięć zostanie zwolniona? Czy jest błąd i wyciek pamięci? Wygląda na to, że jest problem z implementacją.

Moje główne pytanie brzmi: jak mam to odpowiednio wdrożyć?



10
W tym artykule znajdziesz świetną dyskusję na temat implementacji singletonu wraz z bezpieczeństwem wątków w C ++. aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf

106
@sbi - Tylko Sith zajmuje się absolutami. Czy zdecydowaną większość problemów można rozwiązać bez singletonów? Absolutnie. Czy Singletony powodują własne problemy? Tak. Jednak nie mogę szczerze powiedzieć, że są złe , ponieważ projektowanie polega na rozważeniu kompromisów i zrozumieniu niuansów twojego podejścia.
derekerdmann

11
@derekerdmann: Nie powiedziałem, że nigdy nie potrzebujesz zmiennej globalnej (a kiedy jej potrzebujesz, czasami Singleton jest lepszy). Powiedziałem, że powinny być używane w jak najmniejszym stopniu. Uznanie Singletona za cenny wzór projektowy sprawia wrażenie, że warto go używać, a nie hakować , co sprawia, że ​​kod jest trudny do zrozumienia, trudny w utrzymaniu i trudny do przetestowania. Właśnie dlatego zamieściłem swój komentarz. Żadne z twoich dotychczasowych stwierdzeń nie było temu sprzeczne.
sbi

13
@sbi: Powiedziałeś „nie używaj ich”. Nie o wiele bardziej rozsądne „powinny być używane w jak najmniejszym stopniu”, do którego później się zmieniłeś - na pewno widzisz różnicę.
jwd 17.10.11

Odpowiedzi:


1106

W 2008 roku dostarczyłem implementację C ++ 98 wzorca projektowego Singleton, który jest leniwie oceniany, gwarantuje zniszczenie, nie jest technicznie bezpieczny dla wątków:
Czy ktoś może dostarczyć mi próbkę Singletona w c ++?

Oto zaktualizowana implementacja wzoru konstrukcyjnego Singleton w C ++ 11, która jest leniwie oceniana, poprawnie zniszczona i bezpieczna dla wątków .

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
        // ========
        // Don't 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: (niezbyt często)
Singleton: jak go używać

Zobacz dwa artykuły na temat kolejności inicjalizacji i sposobu radzenia sobie z nią:
Kolejność inicjalizacji zmiennych statycznych
Znalezienie problemów z kolejnością inicjalizacji statycznej 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, czy jest bezpieczna dla wątków?

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 ++?
Dr Dobbs: C ++ i The Perils of Double-Checked Locking: Część I


23
Dobra odpowiedź. Należy jednak pamiętać, że nie jest to bezpieczne dla wątków stackoverflow.com/questions/1661529/…
Varuna,

4
@zourtney: Wiele osób nie zdaje sobie sprawy z tego, co właśnie zrobiłeś :)
Johann Gerell

4
@MaximYegorushkin: Kiedy to zostanie zniszczone, jest bardzo dobrze zdefiniowane (nie ma dwuznaczności). Zobacz: stackoverflow.com/questions/246564/...
Martin York

3
What irks me most though is the run-time check of the hidden boolean in getInstance()Jest to założenie dotyczące techniki wdrażania. Nie trzeba zakładać, że żyje. patrz stackoverflow.com/a/335746/14065 Możesz wymusić sytuację, aby zawsze była żywa (mniej narzutów niż Schwarz counter). Zmienne globalne mają więcej problemów z kolejnością inicjowania (pomiędzy jednostkami kompilacji), ponieważ nie wymusza się kolejności. Zaletą tego modelu jest 1) leniwa inicjalizacja. 2) Zdolność do egzekwowania rozkazu (Schwarz pomaga, ale jest brzydszy). Tak, get_instance()jest o wiele brzydszy.
Martin York

3
@kol: Nie. To nie jest zwykły. To, że początkujący kopiują i wklejają kod bez zastanowienia, nie czyni go zwykłym. Zawsze powinieneś spojrzeć na przypadek użycia i upewnić się, że operator przypisania wykonuje to, czego się spodziewasz. Kopiowanie i wklejanie kodu spowoduje błędy.
Martin York,

47

Będąc Singletonem, zwykle nie chcesz, aby został zniszczony.

Zostanie zerwany i zwolniony po zakończeniu programu, co jest normalnym, pożądanym zachowaniem dla singletona. Jeśli chcesz mieć możliwość jawnego wyczyszczenia, dość łatwo jest dodać do klasy metodę statyczną, która pozwala przywrócić ją do stanu czystego i umożliwić jej ponowne przydzielenie przy następnym użyciu, ale jest to poza zakresem „klasyczny” singleton.


4
jeśli usunięcie nigdy nie jest jawnie wywoływane w statycznej instancji Singleton *, czy nie byłoby to technicznie uważane za wyciek pamięci?
Andrew Garrison,

7
To już nie wyciek pamięci, tylko zwykła deklaracja zmiennej globalnej.
ilya n.

15
Mówiąc wprost: „wyciek pamięci” dotyczy singletonów jest zupełnie nieistotny. Jeśli masz stanowe zasoby, w których porządek dekonstrukcji ma znaczenie, singletony mogą być niebezpieczne; ale cała pamięć jest odzyskiwana przez system operacyjny po zakończeniu programu ... niwelując ten całkowicie akademicki punkt w 99,9% przypadków. Jeśli chcesz argumentować gramatykę na temat tego, co jest i nie jest „przeciekiem pamięci”, to dobrze, ale zdaj sobie sprawę, że jest to odwrócenie uwagi od faktycznych decyzji projektowych.
jkerian

12
@jkerian: Wycieki pamięci i zniszczenie w kontekście C ++ tak naprawdę nie polega na wycieku pamięci. Naprawdę chodzi o kontrolę zasobów. W przypadku wycieku pamięci destroctor nie jest wywoływany, a zatem wszelkie zasoby powiązane z obiektem nie są poprawnie zwalniane. Pamięć jest tylko prostym przykładem, którego używamy do nauczania programowania, ale istnieją znacznie bardziej złożone zasoby.
Martin York,

7
@Martin Całkowicie się z tobą zgadzam. Nawet jeśli jedynym zasobem jest pamięć, nadal będziesz mieć problemy z odnalezieniem PRAWDZIWYCH wycieków w twoim programie, jeśli będziesz musiał przedzierać się przez listę przecieków, odfiltrowując te, które „nie mają znaczenia”. Lepiej to wszystko wyczyścić, aby każde narzędzie zgłaszające wycieki zgłaszało tylko te rzeczy, które SĄ problemem.
Dolphin

38

Możesz uniknąć przydziału pamięci. Istnieje wiele wariantów, wszystkie mają problemy w przypadku środowiska wielowątkowego.

Wolę ten rodzaj implementacji (właściwie nie jest powiedziane, że wolę, ponieważ unikam singletonów tak bardzo, jak to możliwe):

class Singleton
{
private:
   Singleton();

public:
   static Singleton& instance()
   {
      static Singleton INSTANCE;
      return INSTANCE;
   }
};

Nie ma dynamicznego przydziału pamięci.


3
W niektórych przypadkach ta leniwa inicjalizacja nie jest idealnym wzorcem do naśladowania. Jednym z przykładów jest to, że konstruktor singletonu przydziela pamięć ze sterty i chcesz, aby alokacja była przewidywalna, na przykład w systemie osadzonym lub w innym ściśle kontrolowanym środowisku. Wolę, gdy wzorzec Singleton jest najlepszym do użycia, aby utworzyć instancję jako statyczny element klasy.
dma

3
Dla wielu większych programów, szczególnie tych z bibliotekami dynamicznymi. Każdy globalny lub statyczny obiekt, który nie jest prymitywny, może powodować awarie / awarie podczas zamykania programu na wielu platformach z powodu problemów z porządkiem niszczenia przy rozładowywaniu bibliotek. Jest to jeden z powodów, dla których wiele konwencji kodowania (w tym Google) zakazuje używania nietrywialnych obiektów statycznych i globalnych.
obecalp

Wydaje się, że statyczna instancja w takiej implementacji ma wewnętrzne powiązanie i będzie miała unikalne i niezależne kopie w różnych jednostkach tłumaczeniowych, co spowoduje zamieszanie i złe zachowanie. Ale widziałem wiele takich wdrożeń, coś mi brakuje?
FaceBro,

Co uniemożliwia użytkownikowi przypisanie tego do wielu obiektów, w których kompilator za kulisami używa własnego konstruktora kopii?
Tony Tannous,

19

Odpowiedź @Loki Astari jest doskonała.

Jednak zdarzają się sytuacje, w których wiele obiektów statycznych musi być w stanie zagwarantować, że singleton nie zostanie zniszczony, dopóki wszystkie statyczne obiekty, które korzystają z singletonu nie będą go już potrzebować.

W takim przypadku std::shared_ptrmożna użyć 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() {}
};

9

Inna alternatywa nieprzydzielająca: stwórz singletona, powiedzmy, klasę C, jak potrzebujesz:

singleton<C>()

za pomocą

template <class X>
X& singleton()
{
    static X x;
    return x;
}

Ani to, ani odpowiedź Cătălin nie jest automatycznie wątkowo bezpieczna w obecnym C ++, ale będzie w C ++ 0x.


Obecnie pod gcc jest wątkowo bezpieczny (i był przez jakiś czas).
Martin York,

13
Problem z tym projektem polega na tym, że jeśli jest używany w wielu bibliotekach. Każda biblioteka ma własną kopię singletonu, z którego korzysta ta biblioteka. Więc nie jest to już singleton.
Martin York,

6

Wśród odpowiedzi nie znalazłem implementacji CRTP, więc oto:

template<typename HeirT>
class Singleton
{
public:
    Singleton() = delete;

    Singleton(const Singleton &) = delete;

    Singleton &operator=(const Singleton &) = delete;

    static HeirT &instance()
    {
        static HeirT instance;
        return instance;
    }
};

Aby użyć, po prostu odziedzicz z tego swoją klasę, na przykład: class Test : public Singleton<Test>


1
Nie mogłem tego uruchomić z C ++ 17, dopóki nie ustawiłem domyślnego konstruktora w postaci ochrony i „= default;”.
WFranczyk

6

Rozwiązanie w przyjętej odpowiedzi ma znaczną wadę - destruktor dla singletonu jest wywoływany po opuszczeniu main()funkcji przez kontrolę . Naprawdę mogą pojawić się problemy, gdy w obiekcie zostaną przydzielone niektóre obiekty zależnemain .

Spotkałem ten problem, gdy próbowałem wprowadzić Singleton w aplikacji Qt. Zdecydowałem, że wszystkie moje okna dialogowe instalacji muszą być singletonami, i zastosowałem powyższy wzór. Niestety, główna klasa Qt QApplicationzostała przydzielona na stosie wmain funkcji, a Qt zabrania tworzenia i niszczenia okien dialogowych, gdy żaden obiekt aplikacji nie jest dostępny.

Dlatego wolę przydzielone sterty singletonów. Podaję wyraźne init()i term()metody dla wszystkich singletonów i nazywam je wewnątrz main. W ten sposób mam pełną kontrolę nad kolejnością tworzenia / niszczenia singletonów, a także gwarantuję, że singletony zostaną utworzone bez względu na to, czy ktoś zadzwonił, getInstance()czy nie.


2
Jeśli odwołujesz się do aktualnie akceptowanej odpowiedzi, pierwsze stwierdzenie jest błędne. Destruktor nie jest wywoływany, dopóki wszystkie obiekty o czasie przechowywania statycznego nie zostaną zniszczone.
Martin York,

5

Oto łatwa implementacja.

#include <Windows.h>
#include <iostream>

using namespace std;


class SingletonClass {

public:
    static SingletonClass* getInstance() {

    return (!m_instanceSingleton) ?
        m_instanceSingleton = new SingletonClass : 
        m_instanceSingleton;
    }

private:
    // private constructor and destructor
    SingletonClass() { cout << "SingletonClass instance created!\n"; }
    ~SingletonClass() {}

    // private copy constructor and assignment operator
    SingletonClass(const SingletonClass&);
    SingletonClass& operator=(const SingletonClass&);

    static SingletonClass *m_instanceSingleton;
};

SingletonClass* SingletonClass::m_instanceSingleton = nullptr;



int main(int argc, const char * argv[]) {

    SingletonClass *singleton;
    singleton = singleton->getInstance();
    cout << singleton << endl;

    // Another object gets the reference of the first object!
    SingletonClass *anotherSingleton;
    anotherSingleton = anotherSingleton->getInstance();
    cout << anotherSingleton << endl;

    Sleep(5000);

    return 0;
}

Utworzono tylko jeden obiekt, a odwołanie do tego obiektu jest zwracane za każdym razem po słowie.

SingletonClass instance created!
00915CB8
00915CB8

Tutaj 00915CB8 to lokalizacja pamięci obiektu Singleton, taka sama przez czas trwania programu, ale (normalnie!) Różna za każdym razem, gdy program jest uruchamiany.

Uwaga: To nie jest wątek bezpieczny. Musisz zapewnić bezpieczeństwo wątku.


5

Jeśli chcesz przydzielić obiekt w stercie, dlaczego nie użyć unikalnego wskaźnika. Pamięć również zostanie zwolniona, ponieważ używamy unikalnego wskaźnika.

class S
{
    public:
        static S& getInstance()
        {
            if( m_s.get() == 0 )
            {
              m_s.reset( new S() );
            }
            return *m_s;
        }

    private:
        static std::unique_ptr<S> m_s;

        S();
        S(S const&);            // Don't Implement
        void operator=(S const&); // Don't implement
};

std::unique_ptr<S> S::m_s(0);

3
Przestarzałe w c ++ 11. zamiast tego zalecane jest unikalne_ptr. cplusplus.com/reference/memory/auto_ptr cplusplus.com/reference/memory/unique_ptr
Andrew

2
To nie jest bezpieczne dla wątków. Lepiej, aby m_slokalny staticz getInstance()i zainicjować go natychmiast bez badania.
Galik

2

Prawdopodobnie jest przydzielany ze stosu, ale bez źródeł nie ma możliwości poznania.

Typowa implementacja (zaczerpnięta z kodu, który już mam w emacsie) to:

Singleton * Singleton::getInstance() {
    if (!instance) {
        instance = new Singleton();
    };
    return instance;
};

... i polegać na tym, że program wykracza poza zakres, aby potem posprzątać.

Jeśli pracujesz na platformie, na której czyszczenie musi być wykonane ręcznie, prawdopodobnie dodam procedurę czyszczenia ręcznego.

Innym problemem związanym z robieniem tego w ten sposób jest to, że nie jest bezpieczny dla wątków. W środowisku wielowątkowym dwa wątki mogłyby przedostać się przez „if”, zanim którekolwiek z nich będzie miało szansę na przydzielenie nowej instancji (tak by oba miały). To nadal nie jest zbyt duża sprawa, jeśli i tak chcesz zakończyć program.


można wywnioskować, ponieważ widać, że zmienna instancji jest wskaźnikiem instancji klasy.
Artem Barger,

3
Nie ma potrzeby dynamicznego przydzielania singletonu. W rzeczywistości jest to zły pomysł, ponieważ nie ma sposobu na automatyczną alokację przy użyciu powyższego projektu. Wypadnięcie poza zasięg nie wywołuje destrukcji i jest po prostu leniwe.
Martin York,

Możesz automatycznie cofnąć przydział za pomocą funkcji atexit. Właśnie to robimy (nie mówiąc, że to dobry pomysł)
Joe

2

Czy ktoś wspomniał std::call_oncei std::once_flag? Większość innych podejść - w tym podwójnie sprawdzone ryglowanie - jest zepsuta.

Jednym z głównych problemów we wdrażaniu wzorca singletonu jest bezpieczna inicjalizacja. Jedynym bezpiecznym sposobem jest ochrona sekwencji inicjalizacji barierami synchronizującymi. Ale same bariery muszą zostać bezpiecznie zainicjowane. std::once_flagto mechanizm gwarantujący bezpieczną inicjalizację.


2

Ostatnio omówiliśmy ten temat w mojej klasie EECS. Jeśli chcesz szczegółowo zapoznać się z notatkami z wykładu, odwiedź http://umich.edu/~eecs381/lecture/IdiomsDesPattsCreational.pdf

Są dwa sposoby, które znam, aby poprawnie utworzyć klasę Singleton.

Pierwszy sposób:

Zaimplementuj to w sposób podobny do tego w przykładzie. Jeśli chodzi o zniszczenie, „Singletony zwykle znoszą czas trwania programu; większość systemów operacyjnych odzyska pamięć i większość innych zasobów po zakończeniu programu, więc istnieje argument za tym, aby się tym nie martwić”.

Jednak dobrą praktyką jest sprzątanie po zakończeniu programu. Dlatego możesz to zrobić za pomocą pomocniczej statycznej klasy SingletonDestructor i zadeklarować to jako przyjaciela w swoim Singleton.

class Singleton {
public:
  static Singleton* get_instance();

  // disable copy/move -- this is a Singleton
  Singleton(const Singleton&) = delete;
  Singleton(Singleton&&) = delete;
  Singleton& operator=(const Singleton&) = delete;
  Singleton& operator=(Singleton&&) = delete;

  friend class Singleton_destroyer;

private:
  Singleton();  // no one else can create one
  ~Singleton(); // prevent accidental deletion

  static Singleton* ptr;
};

// auxiliary static object for destroying the memory of Singleton
class Singleton_destroyer {
public:
  ~Singleton_destroyer { delete Singleton::ptr; }
};

Singleton_destroyer zostanie utworzony podczas uruchamiania programu, a „gdy program się zakończy, wszystkie globalne / statyczne obiekty zostaną zniszczone przez kod zamknięcia biblioteki wykonawczej (wstawiony przez linker), więc narzędzie_destroyer zostanie zniszczone; jego destruktor usunie Singleton, uruchamiając jego burzyciel."

Drugi sposób

Nazywa się to Meyers Singleton, stworzony przez C ++ Wizard Scott Meyers. Po prostu zdefiniuj get_instance () inaczej. Teraz możesz także pozbyć się zmiennej elementu wskaźnika.

// public member function
static Singleton& Singleton::get_instance()
{
  static Singleton s;
  return s;
}

Jest to miłe, ponieważ zwracana wartość jest referencyjna i można użyć .składni zamiast ->dostępu do zmiennych składowych.

„Kompilator automatycznie buduje kod, który po raz pierwszy tworzy„ s ”poprzez deklarację, a nie później, a następnie usuwa obiekt statyczny po zakończeniu programu.”

Zauważ również, że dzięki Meyers Singleton „możesz dostać się w bardzo trudną sytuację, jeśli obiekty polegają na sobie w momencie zakończenia - kiedy Singleton znika w stosunku do innych obiektów? Ale w przypadku prostych aplikacji działa to dobrze”.


1

Oprócz innych dyskusji tutaj warto zauważyć, że możesz mieć globalność bez ograniczania użycia do jednej instancji. Rozważmy na przykład przypadek liczenia czegoś ...

struct Store{
   std::array<Something, 1024> data;
   size_t get(size_t idx){ /* ... */ }
   void incr_ref(size_t idx){ /* ... */}
   void decr_ref(size_t idx){ /* ... */}
};

template<Store* store_p>
struct ItemRef{
   size_t idx;
   auto get(){ return store_p->get(idx); };
   ItemRef() { store_p->incr_ref(idx); };
   ~ItemRef() { store_p->decr_ref(idx); };
};

Store store1_g;
Store store2_g; // we don't restrict the number of global Store instances

Teraz gdzieś wewnątrz funkcji (np. main) Możesz:

auto ref1_a = ItemRef<&store1_g>(101);
auto ref2_a = ItemRef<&store2_g>(201); 

Referencje nie muszą przechowywać wskaźnika z powrotem do swoich odpowiednich, Storeponieważ te informacje są dostarczane w czasie kompilacji. Nie musisz też martwić się o Storeżywotność, ponieważ kompilator wymaga, aby był globalny. Jeśli rzeczywiście jest tylko jeden przypadek Store, w takim podejściu nie ma kosztów ogólnych; z więcej niż jedną instancją kompilator musi być sprytny w generowaniu kodu. Jeśli to konieczne, ItemRefklasa może być nawet wykonana friendzStore (możesz mieć szablonów przyjaciół!).

Jeśli Storesama jest klasą szablonową, robi się bałagan, ale nadal można skorzystać z tej metody, być może poprzez zaimplementowanie klasy pomocniczej o następującej sygnaturze:

template <typename Store_t, Store_t* store_p>
struct StoreWrapper{ /* stuff to access store_p, e.g. methods returning 
                       instances of ItemRef<Store_t, store_p>. */ };

Użytkownik może teraz utworzyć StoreWrappertyp (i instancję globalną) dla każdej Storeinstancji globalnej i zawsze uzyskiwać dostęp do sklepów za pośrednictwem instancji opakowania (w ten sposób zapominając o krwawych szczegółach parametrów szablonu potrzebnych do użycia Store).


0

Chodzi o zarządzanie czasem życia obiektu. Załóżmy, że masz więcej niż singletony w swoim oprogramowaniu. I zależą od singletona Logger. Podczas niszczenia aplikacji załóżmy, że inny obiekt singleton używa Loggera do rejestrowania kroków niszczenia. Musisz zagwarantować, że Logger powinien zostać wyczyszczony na końcu. Dlatego proszę również sprawdzić ten artykuł: http://www.cs.wustl.edu/~schmidt/PDF/ObjMan.pdf


0

Moja implementacja jest podobna do Galika. Różnica polega na tym, że moja implementacja pozwala współużytkowanym wskaźnikom wyczyścić przydzieloną pamięć, w przeciwieństwie do zatrzymywania pamięci do momentu zamknięcia aplikacji i wyczyszczenia wskaźników statycznych.

#pragma once

#include <memory>

template<typename T>
class Singleton
{
private:
  static std::weak_ptr<T> _singleton;
public:
  static std::shared_ptr<T> singleton()
  {
    std::shared_ptr<T> singleton = _singleton.lock();
    if (!singleton) 
    {
      singleton.reset(new T());
      _singleton = singleton;
    }

    return singleton;
  }
};

template<typename T>
std::weak_ptr<T> Singleton<T>::_singleton;

0

Twój kod jest poprawny, z wyjątkiem tego , że nie zadeklarowałeś wskaźnika instancji poza klasą . Wewnętrzne deklaracje klas zmiennych statycznych nie są uważane za deklaracje w C ++, jednak jest to dozwolone w innych językach, takich jak C # lub Java itp.

class Singleton
{
   public:
       static Singleton* getInstance( );
   private:
       Singleton( );
       static Singleton* instance;
};
Singleton* Singleton::instance; //we need to declare outside because static variables are global

Musisz wiedzieć, że instancja Singleton nie musi być przez nas ręcznie usuwana . Potrzebujemy jednego obiektu w całym programie, więc pod koniec wykonywania programu zostanie on automatycznie zwolniony.


-1

Dokument, do którego odsyłano powyżej, opisuje niedociągnięcie podwójnie sprawdzonego blokowania, ponieważ kompilator może przydzielić pamięć dla obiektu i ustawić wskaźnik na adres przydzielonej pamięci przed wywołaniem konstruktora obiektu. W c ++ jest jednak dość łatwe użycie ręcznych alokatorów do ręcznego przydzielenia pamięci, a następnie wywołanie konstrukcyjne w celu zainicjowania pamięci. Przy użyciu tej metody sprawdzanie blokady działa dobrze.


2
Niestety nie. Zostało to szczegółowo omówione przez jednych z najlepszych programistów C ++. Podwójnie zaznaczone blokowanie jest zepsute w C ++ 03.
Martin York,

-1
#define INS(c) private:void operator=(c const&){};public:static c& I(){static c _instance;return _instance;}

Przykład:

   class CCtrl
    {
    private:
        CCtrl(void);
        virtual ~CCtrl(void);

    public:
        INS(CCtrl);

-1

Prosta klasa singletona. Musi to być plik klasy nagłówka

#ifndef SC_SINGLETON_CLASS_H
#define SC_SINGLETON_CLASS_H

class SingletonClass
{
    public:
        static SingletonClass* Instance()
        {
           static SingletonClass* instance = new SingletonClass();
           return instance;
        }

        void Relocate(int X, int Y, int Z);

    private:
        SingletonClass();
        ~SingletonClass();
};

#define sSingletonClass SingletonClass::Instance()

#endif

Uzyskaj dostęp do singletona w ten sposób:

sSingletonClass->Relocate(1, 2, 5);

-3

Myślę, że powinieneś napisać funkcję statyczną, w której twój obiekt statyczny zostanie usunięty. Powinieneś wywołać tę funkcję, gdy masz zamiar zamknąć aplikację. Zapewni to, że nie masz wycieku pamięci.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.