Historia pytania:
Wzorzec Singleton został zaproponowany w celu ograniczenia tworzenia tylko jednego egzemplarza określonej klasy, co było pożądane dla realizacji globalnych menedżerów (np. loggerów, pul zasobów, konfiguratorów).
Problem:
Implementacja Singletona wydaje się prosta, ale w C++ pojawiają się trudności: bezpieczeństwo wątków, poprawność niszczenia, kolejność inicjalizacji.
Rozwiązanie:
Najbezpieczniejszy i najnowocześniejszy sposób implementacji Singletona wykorzystuje statyczną lokalną zmienną wewnątrz statycznej funkcji, co gwarantuje inicjalizację przy pierwszym wywołaniu, bezpieczeństwo wątków (od C++11) oraz poprawne niszczenie.
Przykład kodu:
class Singleton { public: static Singleton& instance() { static Singleton s; return s; } void doSomething() {} private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
Kluczowe cechy:
Czy można stworzyć drugi egzemplarz Singletona za pomocą serializacji lub klonowania?
Tak. Jeśli zaimplementujesz metody serializacji/deserializacji lub ręcznie zaimplementujesz clone(), nie ograniczając konstruktora kopiowania, może pojawić się drugi egzemplarz. Aby tego uniknąć, należy zablokować wszystkie sposoby kopiowania, klonowania i przywracania przez serializację.
Czy Singleton realizowany w środowisku wielowątkowym w C++98/03 będzie poprawny przez lokalną zmienną statyczną?
Nie. Lokalne zmienne statyczne przed C++11 nie gwarantowały bezpieczeństwa wątków przy inicjalizacji. Mogło to prowadzić do utworzenia wielu egzemplarzy, jeśli dwa wątki jednocześnie trafiały do funkcji instance(). W C++11 i nowszych problem ten został rozwiązany na poziomie standardu.
Kiedy niszczony jest egzemplarz Singletona tworzony przez lokalną zmienną statyczną?
Obiekt jest niszczony w odwrotnej kolejności niż został stworzony (LIFO) na etapie zakończenia programu (exit). Może to prowadzić do problemów, jeśli w destruktorze odwołasz się do obiektów już zniszczonych.
W systemie logowania deweloper implementuje Singleton za pomocą globalnego wskaźnika i new, zapominając zablokować kopiowanie. Program działa, ale w środowisku wielowątkowym czasami powstają różne egzemplarze loggera, a wiadomości giną.
Zalety:
Wady:
Singleton zaimplementowano przez lokalną zmienną statyczną w funkcji statycznej, a kopiowanie jest zablokowane. Bezpieczeństwo wątków jest zapewnione, program jest skalowalny, a logi są poprawnie podzielone.
Zalety:
Wady: