C++-Singleton-Entwurfsmuster

Lesezeit: 11 Minuten

C Singleton Entwurfsmuster
Artem Barger

Kürzlich bin ich auf eine Realisierung/Implementierung des Singleton-Entwurfsmusters für C++ gestoßen. Es hat so ausgesehen (ich habe es aus dem realen Beispiel übernommen):

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

Aus dieser Deklaration kann ich ableiten, dass das Instanzfeld auf dem Heap initiiert wird. Das heißt, es gibt eine Speicherzuweisung. Was mir völlig unklar ist, ist, wann genau der Speicher freigegeben wird? Oder gibt es einen Fehler und ein Speicherleck? Es scheint ein Problem mit der Implementierung zu geben.

Meine Hauptfrage ist, wie setze ich es richtig um?

  • Siehe auch: stackoverflow.com/questions/211237/… und stackoverflow.com/questions/270947/… und stackoverflow.com/questions/246564/… und stackoverflow.com/questions/449436/… und stackoverflow.com/questions/335369 /…

    – Martin York

    17. Juni 2009 um 16:43 Uhr

  • In diesem Whitepaper finden Sie eine großartige Erörterung der Implementierung eines Singletons sowie der Thread-Sicherheit in C++. aristeia.com/Papers/DDJ%5FJul%5FAug%5F2004%5Frevised.pdf

    Matthias N.

    30. Oktober 2009 um 11:34 Uhr

  • @sbi – Nur ein Sith handelt absolut. Kann die überwiegende Mehrheit der Probleme ohne Singletons gelöst werden? Absolut. Verursachen Singletons eigene Probleme? Jawohl. Allerdings kann ich nicht ehrlich sagen, dass sie es sind Schlechtda es beim Design darum geht, die Kompromisse zu berücksichtigen und die Nuancen Ihres Ansatzes zu verstehen.

    – derekerdmann

    28. Juli 2011 um 20:10 Uhr

  • @derekerdmann: Ich habe nicht gesagt, dass Sie niemals eine globale Variable benötigen (und wenn Sie eine benötigen, eine Singleton manchmal ist besser). Was ich gesagt habe, ist, dass sie so wenig wie möglich verwendet werden sollten. Die Verherrlichung von Singleton als wertvolles Designmuster erweckt eher den Eindruck, dass es gut ist, es zu verwenden, als dass es eins ist hacken, was den Code schwer verständlich, schwer zu warten und schwer zu testen macht. Aus diesem Grund habe ich meinen Kommentar gepostet. Nichts von dem, was Sie bisher gesagt haben, widersprach dem.

    – sbi

    29. Juli 2011 um 13:26 Uhr

  • @sbi: Was Sie sagten, war “Benutzen Sie sie nicht.” Nicht das viel vernünftigere “sollten so wenig wie möglich verwendet werden”, auf das Sie später umgestellt haben – sicherlich sehen Sie den Unterschied.

    – jwd

    17. Oktober 2011 um 22:21 Uhr

C Singleton Entwurfsmuster
Martin York

2008 habe ich eine C++98-Implementierung des Singleton-Entwurfsmusters bereitgestellt, die lazy-evaluiert, garantiert zerstört und technisch nicht Thread-sicher ist:
Kann mir jemand ein Beispiel für Singleton in c++ geben?

Hier ist eine aktualisierte C++11-Implementierung des Singleton-Entwurfsmusters, das verzögert ausgewertet, korrekt zerstört und Thread-sicher ist.

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 inaccessible(especially from outside), 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
};

Siehe diesen Artikel darüber, wann ein Singleton verwendet wird: (nicht oft)
Singleton: Wie sollte es verwendet werden

Siehe diese beiden Artikel über die Initialisierungsreihenfolge und wie man damit umgeht:
Initialisierungsreihenfolge statischer Variablen
Probleme mit der statischen Initialisierungsreihenfolge von C++ finden

Siehe diesen Artikel, der Lebensdauern beschreibt:
Was ist die Lebensdauer einer statischen Variablen in einer C++-Funktion?

Sehen Sie sich diesen Artikel an, in dem einige Threading-Auswirkungen auf Singletons erörtert werden:
Singleton-Instanz, die als statische Variable der GetInstance-Methode deklariert ist, ist sie Thread-sicher?

Lesen Sie diesen Artikel, der erklärt, warum doppelt geprüfte Sperren in C++ nicht funktionieren:
Was sind all die allgemeinen undefinierten Verhaltensweisen, die ein C++-Programmierer kennen sollte?
Dr. Dobbs: C++ und die Gefahren doppelt geprüfter Sperren: Teil I

  • Gute Antwort. Beachten Sie jedoch, dass dies nicht Thread-sicher ist. stackoverflow.com/questions/1661529/…

    – Varuna

    25. Dezember 2009 um 8:28 Uhr

  • @zourtney: Viele Leute wissen nicht, was du gerade getan hast 🙂

    – Johann Gerell

    4. Januar 2013 um 7:06 Uhr

  • @MaximYegorushkin: Wenn dies zerstört wird, ist sehr gut definiert (es gibt keine Zweideutigkeit). Siehe: stackoverflow.com/questions/246564/…

    – Martin York

    20. Mai 2013 um 20:39 Uhr

  • What irks me most though is the run-time check of the hidden boolean in getInstance() Das ist eine Annahme zur Implementierungstechnik. Es müssen keine Annahmen darüber gemacht werden, dass es lebt. siehe stackoverflow.com/a/335746/14065 Sie können eine Situation erzwingen, damit sie immer am Leben ist (weniger Overhead als Schwarz counter). Globale Variablen haben mehr Probleme mit der Initialisierungsreihenfolge (über Kompilierungseinheiten hinweg), da Sie keine Reihenfolge erzwingen. Der Vorteil dieses Modells ist 1) verzögerte Initialisierung. 2) Fähigkeit, einen Befehl durchzusetzen (Schwarz hilft, ist aber hässlicher). Ja get_instance() ist viel hässlicher.

    – Martin York

    20. Mai 2013 um 23:07 Uhr

  • @kol: Nein. Es ist nicht das Übliche. Nur weil Anfänger Code ohne nachzudenken kopieren und einfügen, ist es noch lange nicht das Übliche. Sie sollten sich immer den Anwendungsfall ansehen und sicherstellen, dass der Zuweisungsoperator das tut, was erwartet wird. Das Kopieren und Einfügen von Code führt zu Fehlern.

    – Martin York

    12. Oktober 2014 um 16:57 Uhr

Als Singleton möchten Sie normalerweise nicht, dass es zerstört wird.

Es wird abgebaut und freigegeben, wenn das Programm beendet wird, was das normale, gewünschte Verhalten für einen Singleton ist. Wenn Sie es explizit bereinigen möchten, ist es ziemlich einfach, der Klasse eine statische Methode hinzuzufügen, mit der Sie sie in einen sauberen Zustand zurückversetzen und bei der nächsten Verwendung neu zuweisen können, aber das liegt außerhalb des Bereichs von a “klassischer” Singleton.

  • Es ist kein Speicherleck mehr als eine einfache Deklaration einer globalen Variablen.

    – Ilja n.

    17. Juni 2009 um 16:14 Uhr

  • Um etwas klarzustellen … Bedenken hinsichtlich “Speicherlecks” gegenüber Singletons sind völlig irrelevant. Wenn Sie über zustandsbehaftete Ressourcen verfügen, bei denen die Dekonstruktionsreihenfolge wichtig ist, können Singletons gefährlich sein. aber der gesamte Speicher wird vom Betriebssystem beim Beenden des Programms sauber wiedererlangt … was diesen völlig akademischen Punkt in 99,9% der Fälle zunichte macht. Wenn Sie die Grammatik hin und her diskutieren wollen, was ein “Speicherleck” ist und was nicht, ist das in Ordnung, aber erkennen Sie, dass es eine Ablenkung von tatsächlichen Designentscheidungen ist.

    – jkerian

    17. Juni 2009 um 16:21 Uhr

  • @jkerian: Bei Speicherlecks und -zerstörung im C++-Kontext geht es nicht wirklich um Speicherlecks. Eigentlich geht es um Ressourcenkontrolle. Wenn Sie Speicher verlieren, wird der Destruktor nicht aufgerufen und daher werden alle Ressourcen, die dem Objekt zugeordnet sind, nicht korrekt freigegeben. Das Gedächtnis ist nur das einfache Beispiel, das wir verwenden, wenn wir das Programmieren unterrichten, aber es gibt viel komplexere Ressourcen da draußen.

    – Martin York

    17. Juni 2009 um 16:34 Uhr

  • @Martin da stimme ich dir voll und ganz zu. Selbst wenn die einzige Ressource Speicher ist, werden Sie immer noch Schwierigkeiten bekommen, wenn Sie versuchen, ECHTE Lecks in Ihrem Programm zu finden, wenn Sie sich durch eine Liste von Lecks wühlen müssen, um diejenigen herauszufiltern, die “nicht wichtig” sind. Es ist besser, diese alle zu bereinigen, damit jedes Tool, das Lecks meldet, nur Dinge meldet, die ein Problem SIND.

    – Delfin

    17. Juni 2009 um 16:53 Uhr

  • Es ist vage eine Überlegung wert, dass es C++-Implementierungen (möglicherweise sogar gehostete) gibt, in denen das “Betriebssystem” nicht alle Ressourcen wiederherstellen, wenn Ihr Programm beendet wird, die aber ein Konzept haben, “Ihr Programm erneut auszuführen”, wodurch Sie einen neuen Satz globaler und statischer lokaler Ressourcen erhalten. Auf solchen Systemen ist ein nicht freigegebenes Singleton nach jeder vernünftigen Definition ein echtes Leck: Wenn Ihr Programm oft genug ausgeführt wird, wird es das System herunterfahren. Ob Sie sich um die Portierbarkeit auf solche Systeme kümmern, ist eine andere Sache — solange Sie keine Bibliothek schreiben, tun Sie das mit ziemlicher Sicherheit nicht.

    – Steve Jessop

    16. Januar 2014 um 10:04 Uhr


Sie könnten die Speicherzuweisung vermeiden. Es gibt viele Varianten, die alle Probleme in einer Multithreading-Umgebung haben.

Ich bevorzuge diese Art der Implementierung (eigentlich wird nicht richtig gesagt, dass ich bevorzuge, weil ich Singletons so weit wie möglich vermeide):

class Singleton
{
private:
   Singleton();

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

Es hat keine dynamische Speicherzuweisung.

  • In einigen Fällen ist diese verzögerte Initialisierung nicht das ideale Muster. Ein Beispiel ist, wenn der Konstruktor des Singletons Speicher aus dem Heap zuweist und Sie möchten, dass diese Zuweisung vorhersagbar ist, beispielsweise in einem eingebetteten System oder einer anderen streng kontrollierten Umgebung. Wenn das Singleton-Muster das beste Muster ist, ziehe ich es vor, die Instanz als statisches Mitglied der Klasse zu erstellen.

    – DMA

    17. Juni 2009 um 17:06 Uhr

  • Für viele größere Programme, insbesondere solche mit dynamischen Bibliotheken. Jedes globale oder statische Objekt, das nicht primitiv ist, kann auf vielen Plattformen beim Beenden des Programms zu Segfaults/Abstürzen führen, da beim Entladen von Bibliotheken Probleme mit der Reihenfolge der Zerstörung auftreten. Dies ist einer der Gründe, warum viele Programmierkonventionen (einschließlich der von Google) die Verwendung von nicht-trivialen statischen und globalen Objekten verbieten.

    – Obekal

    17. Juni 2009 um 18:04 Uhr

  • Es scheint, dass die statische Instanz in einer solchen Implementierung eine interne Verknüpfung hat und einzigartige und unabhängige Kopien in verschiedenen Übersetzungseinheiten haben wird, was zu verwirrendem und falschem Verhalten führen wird. Aber ich habe viele solcher Implementierungen gesehen, übersehe ich etwas?

    – FaceBro

    25. Februar 2017 um 14:12 Uhr

  • Was hindert den Benutzer daran, dies mehreren Objekten zuzuweisen, bei denen der Compiler hinter den Kulissen seinen eigenen Kopierkonstruktor verwendet?

    – Tony Tannous

    15. Juli 2019 um 20:46 Uhr

  • @Tony Nichts verhindert das Kopieren, du hast Recht. Der Kopierkonstruktor sollte gelöscht werden.

    – ebrahim

    23. Mai 2021 um 17:24 Uhr


C Singleton Entwurfsmuster
Galik

@Loki Astaris Antwort ist ausgezeichnet.

Es gibt jedoch Zeiten mit mehreren statischen Objekten, in denen Sie sicherstellen müssen, dass die Einzelling werden nicht zerstört, bis alle Ihre statischen Objekte, die die verwenden, zerstört werden Einzelling brauche es nicht mehr.

In diesem Fall std::shared_ptr kann verwendet werden, um die zu halten Einzelling lebendig für alle Benutzer, auch wenn die statischen Destruktoren am Ende des Programms aufgerufen werden:

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() {}
};

Eine weitere nicht zuordnende Alternative: Erstellen Sie ein Singleton, beispielsweise eine Klasse Cwie Sie es brauchen:

singleton<C>()

verwenden

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

Weder diese noch Cătălins Antwort sind in aktuellem C++ automatisch Thread-sicher, werden es aber in C++0x sein.

  • Derzeit ist es unter gcc threadsicher (und das schon seit einiger Zeit).

    – Martin York

    17. Juni 2009 um 16:26 Uhr

  • Das Problem bei diesem Design besteht darin, dass es über mehrere Bibliotheken hinweg verwendet wird. Jede Bibliothek hat eine eigene Kopie des Singletons, das diese Bibliothek verwendet. Es ist also kein Singleton mehr.

    – Martin York

    17. Juni 2009 um 16:27 Uhr


Ich habe unter den Antworten keine CRTP-Implementierung gefunden, also hier ist sie:

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;
    }
};

Um es zu verwenden, erben Sie einfach Ihre Klasse davon, wie: class Test : public Singleton<Test>

  • Derzeit ist es unter gcc threadsicher (und das schon seit einiger Zeit).

    – Martin York

    17. Juni 2009 um 16:26 Uhr

  • Das Problem bei diesem Design besteht darin, dass es über mehrere Bibliotheken hinweg verwendet wird. Jede Bibliothek hat eine eigene Kopie des Singletons, das diese Bibliothek verwendet. Es ist also kein Singleton mehr.

    – Martin York

    17. Juni 2009 um 16:27 Uhr


1647303012 139 C Singleton Entwurfsmuster
Letzter Schlag

Hier ist eine einfache Implementierung.

#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;
}

Es wird nur ein Objekt erstellt und diese Objektreferenz wird jedes Mal danach zurückgegeben.

SingletonClass instance created!
00915CB8
00915CB8

Hier ist 00915CB8 der Speicherort des Singleton-Objekts, der für die Dauer des Programms gleich ist, aber (normalerweise!) Jedes Mal, wenn das Programm ausgeführt wird, anders ist.

NB Dies ist kein Thread-Safe. Sie müssen Thread-Sicherheit gewährleisten.

1003460cookie-checkC++-Singleton-Entwurfsmuster

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy