900 Stimmen

C++ Singleton-Entwurfsmuster

Kürzlich bin ich auf eine Realisierung/Implementierung des Singleton-Designmusters für C++ gestoßen. Sie sah wie folgt aus (ich habe sie 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 Erklärung kann ich ableiten, dass das Instanzfeld auf dem Heap initiiert wird. Das heißt, es findet eine Speicherzuweisung statt. Was für mich völlig unklar ist, ist, wann genau der Speicher wieder freigegeben werden soll? Oder gibt es einen Fehler und ein Speicherleck? Es scheint, als gäbe es ein Problem mit der Implementierung.

Meine Hauptfrage ist: Wie setze ich es richtig um?

1401voto

Martin York Punkte 245363

Im Jahr 2008 habe ich eine C++98-Implementierung des Singleton-Entwurfsmusters bereitgestellt, die faul ausgewertet wird, eine garantierte Zerstörung aufweist und technisch gesehen 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, die faul ausgewertet wird, korrekt zerstört wird und thread-safe .

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 man ein Singleton verwenden sollte: (nicht oft)
Singleton: Wie sollte es verwendet werden?

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

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

Siehe diesen Artikel, der einige Auswirkungen des Threading auf Singletons diskutiert:
Ist eine Singleton-Instanz, die als statische Variable der GetInstance-Methode deklariert wird, thread-sicher?

Siehe diesen Artikel, der erklärt, warum doppelt geprüftes Sperren in C++ nicht funktioniert:
Was sind die häufigsten nicht definierten Verhaltensweisen, die ein C++-Programmierer kennen sollte?
Dr. Dobbs: C++ und die Gefahren von Double-Checked Locking: Teil I

68voto

Cătălin Pitiș Punkte 13785

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

Ich bevorzuge diese Art der Implementierung (eigentlich ist es nicht richtig, dass ich sie bevorzuge, denn ich vermeide Singletons so weit wie möglich):

class Singleton
{
private:
   Singleton();

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

Es hat keine dynamische Speicherzuweisung.

55voto

Reed Copsey Punkte 536986

Da es sich um ein Singleton handelt, möchten Sie normalerweise nicht, dass es vernichtet wird.

Es wird abgerissen und freigegeben, wenn das Programm beendet wird, was das normale, gewünschte Verhalten für ein Singleton ist. Wenn Sie in der Lage sein wollen, es explizit zu säubern, ist es ziemlich einfach, der Klasse eine statische Methode hinzuzufügen, die es Ihnen erlaubt, es in einen sauberen Zustand zu versetzen und es bei der nächsten Verwendung neu zuzuordnen, aber das liegt außerhalb des Bereichs eines "klassischen" Singletons.

44voto

Galik Punkte 44681

Antwort von @Loki Astari ist ausgezeichnet.

Es gibt jedoch Fälle, in denen Sie bei mehreren statischen Objekten sicherstellen müssen, dass die Einzelperson wird erst dann zerstört, wenn alle statischen Objekte, die die Einzelperson nicht mehr benötigen.

In diesem Fall std::shared_ptr kann verwendet werden, um die Einzelperson für alle Benutzer am Leben, 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() {}
};

16voto

James Hopkin Punkte 13389

Eine weitere nicht-allokierende Alternative: Erstellen Sie ein Singleton, beispielsweise der Klasse C wie Sie es brauchen:

singleton<C>()

mit

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

Weder diese noch Catalins Antwort ist im aktuellen C++ automatisch thread-sicher, wird es aber in C++0x sein.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X