Singleton-Frage



  • Hallo!

    Als ich vor kurzem angefangen habe, mich mit Singletons zu beschäftigen, bin ich auf folgendes Script gestoßen:

    #include <SDL.h>
    
    template <class T>
    class TSingleton
    {
    	protected:
    		static T *m_pSingleton;
    
    	public:
    		static T* Get()
    		{
    			if (!m_pSingleton)
    				m_pSingleton = new T;
    
    			return (m_pSingleton);
    		}
    
    		static void Del()
    		{
    			if (m_pSingleton)
    			{
    				delete (m_pSingleton);
    				m_pSingleton = NULL;
    			}
    		}
    };
    
    template <class T>
    T* TSingleton<T>::m_pSingleton = 0;
    
    class CTimer : public TSingleton<CTimer>
    {
    };
    

    Die Variable "m_pSingleton" ist wohl das Objekt, das man nur 1 einziges Mal haben will.
    Beim Bau eines Timers für ein Spiel muss der Timer natürlich auch ein Singleton sein. Das ist mir klar, die Funktionen in der Singleton-Klasse hab ich auch verstanden. Dieses Script funktioniert auch.

    Aber da gibt es eine Sache, die ich nicht verstehe:

    der Timer erbt von der Singleton-Klasse, wobei das Singleton auch vom Typ "CTimer" ist.
    Dann müsste die Klasse CTimer ja eine Instanz von sich selber beinhalten! Oder hängt das damit zusammen, dass das Singleton eine statische Membervariable ist, denn statische Membervariablen existieren ja nur 1 mal pro Klasse. Doch iwie versteh ich das nicht. Wenn der Timer wirklich eine Variable vom eigenen Typ enthält, dann müsste diese Variable ja wiederum eine Variable enthalten und das würde unendlich weitergehen. So kann das doch nicht sein. Oder wird das Singleton iwie limitiert, weil es "static" ist ?

    Für Ratschläge bedanke ich mich im Voraus.

    Freundliche Grüße



  • Die Singleton-Instanz ist statisch und existiert deshalb nur einmal für die gesamte Klasse. (das heißt, genau genommen ist die Instanz selber nichtmal statisch, sondern nur ein Zeiger auf diese Instanz - das Objekt wird auf dem Heap angelegt, wenn du es benötigst)

    Aber imho macht es keinen Sinn, einen Timer als einmalig verfügbares Objekt anzulegen. Und selbst wenn, habe ich schon stabilere/robustere Varianten gesehen, um einen Singleton umzusetzen.



  • Hallo!

    Erstmal Danke für die schnelle Antwort.

    Worauf ich auch hinaus wollte ist:

    der Zeiger, den du genannt hast ist ja statisch und existiert nur einmal pro Klasse.
    Der Zeiger ist jedoch hier vom Typ "*CTimer". Da die Klasse CTimer aber an sich von der Singleton-Klasse erbt, müsste dieser Zeiger doch wiederum einen Zeiger auf einen Timer enthalten usw usw.

    class CTimer : public TSingleton<CTimer>
    {
    }; // wird hier die Variable "static T *m_pSingleton" auch vererbt ?
    // wenn ja, dann müsste CTimer ja folgendermaßen aussehen:

    class CTimer : public TSingleton<CTimer>
    {
    static CTimer *m_pSingleton;
    // ... und noch die anderen Funktion Get() und Del()
    };

    Dann wäre das ja ein Timer in einem Timer und dieser Timer hätte dann wiederum einen Timer in sich.

    Oder ist das limitiert, weil die Variable CTimer statisch gehalten ist ?

    Danke im Voraus und Freundliche Grüße



  • [cpp]
    class CTimer : public TSingleton<CTimer>
    {
    static CTimer *m_pSingleton;
    static void Del()...;
    static void Get()...;
    };
    [cpp]

    Werden diese statischen Funktionen und Variablen "mitvererbt" ?

    Meines Erachtens würde das so keinen Sinn machen, weil die Variablen 1. statisch sind und 2. dann in der Klasse CTimer wiederum eine Instanz von sich selbst wäre und das würde dann immer so weitergehen. Oder wird das dadurch verhindert, dass die Variablen und Funktionen statisch sind ?



  • class CTimer : public TSingleton<CTimer>
    {
        static CTimer *m_pSingleton;
        static void Del()...;
        static void Get()...;
    };
    

    Werden diese statischen Funktionen und Variablen "mitvererbt" ?

    Meines Erachtens würde das so keinen Sinn machen, weil die Variablen 1. statisch sind und 2. dann in der Klasse CTimer wiederum eine Instanz von sich selbst wäre und das würde dann immer so weitergehen. Oder wird das dadurch verhindert, dass die Variablen und Funktionen statisch sind ?



  • Oder kann der Timer auf diese statischen Memberfunktionen zugreifen, ohne sie selber zu "beinhalten" ? Dass der Timer sozusagen die Klasse "TSingleton" anspricht und ihr sagt, dass sie eine neue Instanz von "CTimer" erzeugen soll ?

    Viele Grüße und Danke im Voraus



  • Dieser Trick nennt sich "curiously recurring template pattern" (CRTP), s. z.B. http://en.academic.ru/dic.nsf/enwiki/2371256



  • Ja, die statischen Methoden von Singleton<CTimer> werden auch an die Klasse CTimer weitervererbt und sind dort auch verfügbar. Allerdings sind sie immer noch static und deshalb gibt es sie im gesamten Programm genau einmal (die einzelnen CTimer-Objekte, allen voran der in der Get()-Methode erzeugte, können zwar darauf zugreifen, benötigen aber keinen Platz dafür im Inneren).



  • OK

    Das Objekt static T *m_pSingleton wird dann doch auch als "static CTimer *m_pSingleton" vererbt.

    class CTimer : public TSingleton<CTimer>
    {
        static CTimer *m_pSingleton;
        static void Del()...;
        static void Get()...;
    };
    

    Zumindest verfügt dann doch jede Instanz von "CTimer" über diese Funktionen.

    Da aber die statische Variable "*m_pSingleton" auch in CTimer drin ist, aber vom gleichen Typ ist, müsste man doch auch z.B. dann im Hauptprogramm schreiben können:

    int main()
    {
       CTimer MyTimer;
       MyTimer.(*m_pSingleton.(*m_pSingleton)); // Und das hier unendlich oft
    
       return 0;
    }
    

    Mir ist schon klar, dass der Timer natürlich auf alle statischen Methoden zugreifen kann (Del() und Get()). Doch welches Objekt ist nun das Singleton ?

    Die Variable "MyTimer" oder die Variable "MyTimer.(*m_pSingleton)..." ?

    Danke im Voraus und Freundliche Grüße



  • Um mal alle Klarheiten zu beseitigen, versuche ich mal ins Detail zu gehen: Du hast dort insgesamt drei "Objekte" in drei verschiedenen Speicherbereichen:

    1. Die lokale Variable "MyTimer" liegt auf dem Stack und wird in der main-Funktion angelegt.
      (btw, schon die Tatsache, daß du die anlegen kannst, widerspricht dem Prinzip hinter einem Singleton)

    2. MyTimer.m_pSingleton (alias CTimer::m_pSingleton alias Singleton<CTimer>::m_pSingleton) ist ein statischer Zeiger, der bei Programmstart angelegt und mit NULL initialisiert wird. Der gehört zwar zur Klasse, liegt aber außerhalb.

    3. *(MyTimer.m_pSingleton) ist ein weiteres CTimer-Objekt, das von Get() auf dem Heap angelegt wurde und dessen Adresse in m_pSingleton zwischengelagert wird.
      (technisch gesehen ist das deine Singleton-Instanz)

    MyTimer.m_pSingleton->m_pSingleton und ähnliche Konstruktionen verweisen wieder im Kreis auf den Zeiger aus (2).

    PS: Und jetzt können wir uns wieder den wichtigen Problemen zuwenden, z.B. der Frage, wozu man einen Singleton wirklich verwenden sollte 😉



  • OK Jetzt verstehe ich:

    meine Singleton-Variable ist statisch und deshalb ist die "Tiefe" immer 1:

    class CTimer : public TSingleton<CTimer>
    {
        static CTimer *m_pSingleton;  // dies ist die tatsächliche Singleton-Instanz
        static void Del()...;
        static void Get()...;
    };
    

    Hier ist also die statische Membervariable in meiner Klasse CTimer die tatsächliche Singleton-Instanz ?

    Und alle "tieferen" Variablen vom Typ "CTimer" zeigen auf diese immer gleiche Singleton-Instanz ? Dann wird also immer auf die statische Membervariable in meiner Klasse "CTimer" gezeigt. Und diese statische Membervariable ist das eigentliche Singleton.

    Die Funktionen "Get()" und "Del()" bearbeiten also diese statische Membervariable "*m_pSingleton" die in meiner Klasse "CTimer" drinsteht.

    Und wenn man dann schreibt:
    CTimer::Get(), dann wird immer die Singleton-Instanz, also die Instanz des Timers, zurückgegeben.

    Danke für die Hilfe und Gruß
    anonym83


Anmelden zum Antworten