Problemhintergrund:
Das Singleton-Muster wurde vorgeschlagen, um die Erstellung nur einer Instanz einer bestimmten Klasse zu beschränken, was für die Implementierung globaler Manager (z.B. Logger, Ressourcenpools, Konfiguratoren) erforderlich war.
Problem:
Die Implementierung eines Singletons scheint einfach zu sein, aber in C++ gibt es Schwierigkeiten: Thread-Sicherheit, korrekte Zerstörung, Initialisierungsreihenfolge.
Lösung:
Die sicherste und modernste Möglichkeit, ein Singleton zu implementieren, verwendet eine statische lokale Variable innerhalb einer statischen Funktion, was die Initialisierung bei der ersten Verwendung, Thread-Sicherheit (ab C++11) und korrekte Zerstörung garantiert.
Codebeispiel:
class Singleton { public: static Singleton& instance() { static Singleton s; return s; } void doSomething() {} private: Singleton() {} Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; };
Schlüsselfunktionen:
Kann man eine zweite Instanz des Singletons durch Serialisierung oder Klonen erstellen?
Ja. Wenn Sie Methoden zur Serialisierung/Deserialisierung implementieren oder manuell clone() implementieren, ohne den Kopierkonstruktor zu verbieten, kann eine zweite Instanz entstehen. Um dies zu vermeiden, müssen alle Arten des Kopierens, Klonens und der Wiederherstellung durch Serialisierung verboten werden.
Wird das Singleton in einer Multithread-Umgebung in C++98/03 korrekt durch eine lokale statische Variable implementiert?
Nein. Lokale statische Variablen vor C++11 gewährten keine Thread-Sicherheit bei der Initialisierung. Dies konnte zur Erstellung mehrerer Instanzen führen, wenn zwei Threads gleichzeitig die Funktion instance() aufriefen. In C++11 und neuer wurde das Problem auf Standardebene gelöst.
Wann wird die Instanz des Singletons, die über eine statische lokale Variable erstellt wurde, zerstört?
Das Objekt wird in umgekehrter Reihenfolge der Erstellung (LIFO) beim Programmabschluss (exit) zerstört. Dies kann zu Problemen führen, wenn im Destruktor auf bereits zerstörte Objekte zugegriffen wird.
In einem Protokollierungssystem implementiert der Entwickler das Singleton mithilfe eines globalen Zeigers und new, vergisst jedoch, das Kopieren zu verbieten. Das Programm funktioniert, aber in einer Multithread-Umgebung treten manchmal verschiedene Instanzen des Loggers auf, Nachrichten gehen verloren.
Vorteile:
Nachteile:
Das Singleton wird über eine statische lokale Variable in einer statischen Funktion implementiert, das Kopieren ist verboten. Thread-Sicherheit ist gewährleistet, das Programm skaliert, Protokolle werden korrekt geteilt.
Vorteile:
Nachteile: