singelton und destruktor?



  • Habe hier ein auszug ein Singelton klasse:

    class Singleton
    {
       public:
         static Singleton* exemplar();
    
       protected:
         Singleton() {}
    
       private:
         static Singleton *instanz;
    };
    
    Singleton* Singleton::instanz = 0;
    
    Singleton* Singleton::exemplar()
    {
      if( instanz == 0 )
        instanz = new Singleton();
      return instanz;
    }
    

    über die statischer funktion exemplar() bekomm ich ja ein zeiger auf das singelton objekt... schön und gut, aber muss ich jetzt das objekt auch extra deleten? quasie ne DEstroyExemplar funktion mache? oder gibts andere möglichkeiten?



  • wozu?
    ein singleton wird mit dem beenden des programms destruiert ...

    das erzeugt zwar ein memory-leak, was dich aber nicht stören sollte,
    da das programm dann eh beendet ist (OS räumt auf, da haste deinen speicher ja
    auch her)

    wenn es dich doch stört (valgrind gibt dann zB fehlermeldungen, gibt es noch
    ein singleton mit referencen, dort tritt das "quasi-leck" dann nichtmehr auf)



  • Wie wär's mit einem statischen "Aufräumer" ?

    struct Aufräumer;
    class Singleton
    {
    // ... Dein Code ...
    
    // zusätzlich:
       protected:
         ~Singleton() {} // Nicht unwichtig ! Schließlich soll kein "Nutzer" das Teil abräumen können
       friend Aufräumer;
    };
    
    struct Aufräumer {
       ~Aufräumer() { delete Singleton::instanz; }
    }
    
    static Aufräumer a;
    

    Bei static Objekten sorgte IIRC der Compiler dafür, dass sie ganz am Ende abgeräumt werden (wenn überhaupt unwinding stattfindet).

    Gruß,

    Simon2.



  • Treb schrieb:

    OS räumt auf

    da wär ich mir nicht so sicher...

    @BorisDieKlinge:

    //...
    public:
      ~Singleton() {
          if(instanz) {
              delete instanz;
              instanz = 0;
          }
       }
    //...
    


  • schön und gut, aber muss ich jetzt das objekt auch extra deleten?

    Hast du es mit new erstellt?
    Sieht ganz so aus - also wirst du es auch löschen müssen...


  • Mod

    Xantus_notloggedin schrieb:

    @BorisDieKlinge:

    //...
    public:
      ~Singleton() {
          if(instanz) {
              delete instanz;
              instanz = 0;
          }
       }
    //...
    

    Dieser Destruktor wird nicht automatisch aufgerufen werden.
    Würde man ihn aufrufen, führte das nur zu mehrfachem delete (in diesem Falle wahrscheinlich beobachtbar als Stacküberlauf wegen Endlosrekursion). Letzteres könnte man noch umgehen

    if(instanz) {
              Singleton* p = instanz;
              instanz = 0;
              delete p;
          }
    

    (allerdings immer noch doppelte Zerstörung) und es würde aber eben trotzdem nicht automatisch aufgeräumt werden, denn automatisch aufgeräumt werden statische Objekte - statisch ist in unserem Falle aber der zeiger und nicht das Klassenobjekt selbst.
    Anzumerken wäre noch, dass die Zerstörung und Freigabe von Objekten nicht explizit erforderlich - wenn und solange man sich nicht auf die Seiteneffekte dieser Zerstörung und Freigabe verlässt. Es ist also nicht per se undefiniert, den Singleton einfach leben zu lassen.



  • wenn es dich doch stört (valgrind gibt dann zB fehlermeldungen, gibt es noch
    ein singleton mit referencen, dort tritt das "quasi-leck" dann nichtmehr auf)

    wie würde ein singelton mit objektrefenrz ausehen?



  • Xantus_notloggedin schrieb:

    Treb schrieb:

    OS räumt auf

    da wär ich mir nicht so sicher...

    Windows macht das z.B.

    Xantus_notloggedin schrieb:

    @BorisDieKlinge:

    //...
    public:
      ~Singleton() {
          if(instanz) {
              delete instanz;
              instanz = 0;
          }
       }
    //...
    

    Sehr clever, ja... Nur wird der Destruktor halt nicht aufgerufen.
    Eine Lösung wäre folgende:

    class Singleton
    {
    public:
       static Singleton* exemplar()
       {
    	   if( instanz == 0 )
    	   {
    		instanz = new Singleton();
    		atexit( &Singleton::destroy );
    	   }
    	   return instanz;
       }
    
       static void destroy()
       {
    	delete instanz;
       }
    
       protected:
         Singleton() {}
    
       private:
         static Singleton *instanz;
    };
    
    Singleton* Singleton::instanz = 0;
    


  • ich benutzt es meistens so in der art, ist praktisch da man dann nur von Singleton ableiten muss und dann ne recht kleine uebersichtliche klasse hat - man muss nur sofern man das singleton verwendete - dispose aufrufen um den speicher frei zu geben,
    achso und man kann gut funktionsueberladungen erzwingen #gg
    Singleton.h

    template <class T>
    class Singleton
    {
    private:
    	static T* _instance;
    protected:
    	Singleton(){}
    public:
    	static T* instance()
    	{
    		if(!_instance)
    			_instance = new T();
    		return _instance;
    	}
    	static void dispose()
    	{
    		delete _instance;
    		_instance = 0;
    	}
    	virtual ~Singleton()
    	{
    		_instance = 0;
    	}
    };
    template <class T>
    T* Singleton<T>::_instance = 0;
    

    Using.h

    class Using : public Singleton<Using>
    {
    private:
    	friend class Singleton<Using>;
    
    protected:
    	virtual ~Using(void)
    	{
    		close();
    	}
    public:
    	static Using *instance()
    	{
    		return Singleton<Using>::instance();
    	}
    };
    

    .
    .
    Using::instance()->func();
    Using::instance()->dispose();



  • Wann wird die atexit funktion aufrufen?

    atexit( &Singleton::destroy );
    

    ??

    Was ist mit dem:

    static void Singelton::Destroy(){
      if( instanz) delete instanz;
    }
    
    {
    Singelton *p= exemplar();
    .
    .
    .
    .
    p->Destroy();
    
    }
    


  • Okay, welchen Sinn hat das mit dem Zeiger eigentlich? Wieso verwendest Du nicht folgenden Code:

    static singleton& instance() {
        static singleton the_instance;
        return the_instance;
    }
    


  • Viele wege füheren nach rom.. aber genau diesen hab ich gesucht^^
    hätte ich auch selber drauf kommen können 👎



  • Wenn man selbst Kontrolle über die Lebenszeit von Singletons haben will, kommt man fast nicht darum, die Persistenz mit atexit() selbst zu kontrollieren.



  • Tachyon schrieb:

    Wenn man selbst Kontrolle über die Lebenszeit von Singletons haben will, kommt man fast nicht darum, die Persistenz mit atexit() selbst zu kontrollieren.

    äh ... was spricht gegen meinen "Abräumer" ?

    Konrad Rudolph schrieb:

    Okay, welchen Sinn hat das mit dem Zeiger eigentlich? Wieso verwendest Du nicht folgenden Code:

    static singleton& instance() {
        static singleton the_instance;
        return the_instance;
    }
    

    Das geht bestimmt oftmals gut - aber "lazy creation" kann man damit halt nicht machen (nur, für den Fall, dass man es braucht).
    und wie sieht's aus, wenn man ein parametrisiertes instance() anbieten möchte ? (wobei das Schwierigkeiten beim 2. Aufruf bereitet)

    Gruß,

    Simon2.



  • , dass man dann nicht selbst die kontrolle hat?



  • Simon2 schrieb:

    Tachyon schrieb:

    Wenn man selbst Kontrolle über die Lebenszeit von Singletons haben will, kommt man fast nicht darum, die Persistenz mit atexit() selbst zu kontrollieren.

    äh ... was spricht gegen meinen "Abräumer" ?

    Gruß,

    Simon2.

    Das Dead-Refference-Problem. Es kann sein, dass der Destruktor von Deinem Aufräumer getriggert wird, dass Singleton aber noch benötigt wird.



  • hmm vlt. schrieb:

    , dass man dann nicht selbst die kontrolle hat?

    😕
    DAS ist ja gerade der Vorteil von RAII: Kontrolle an die Runtime abgeben, damit man es selbst nicht vergessen kann.

    Wer unbedingt "die Kontrolle behalten" will, sollte Assembler programmieren...
    😉

    Gruß,

    Simon2.



  • Tachyon hats schon gesagt. Da braucht man kein Assembler.



  • Tachyon schrieb:

    ...Das Dead-Refference-Problem...

    wohlklingender Name - hatte ich noch nicht gehört. 😉

    Natürlich kann man sich da in Abhängigkeiten verheddern (von denen mir allerdings momentan nur welche einfallen, die unter "schlechtes Design" fallen).
    Aber das geht Dir mit atexit() nicht anders: Auch hier muss jede Form von Abhängigkeit berücksichtigt werden ... ob ich das nun im "Abräumer" mache oder in atexit(), sollte IMHO keinen großen Unterschied machen.
    Da hat der Abräumer noch den Vorteil, dass ich auf den ja zur Laufzeit auch noch Zugriff habe, so dass Objekte ihre Abhängigkeiten da hinterlegen können.

    Gruß,

    Simon2.



  • hmm vlt. schrieb:

    Tachyon hats schon gesagt. Da braucht man kein Assembler.

    Habe ich behauptet, dass man zur Lösung dieses Problems Assembler braucht ?
    Übrigens: Wenn Du keine blöden Antworten haben möchtest, solltest Du evtl. dazu übergehen, Deine Standpunkte verständlich darzulegen.
    Wen jemand in "Ein-Satz-Statements" kommuniziert, ist nicht immer die Umwelt schuld, wenn sie einen mißversteht.

    Gruß,

    Simon2.


Anmelden zum Antworten