privater Destruktor



  • Ich hab grad noch mal in Scott Meyers' More effective C++ nachgeschaut und bin über einen Möglichkeit gestolpert, wie man das erzeugen von Objekten auf dem Stack verhindert, dort meint er, man könbne den Desturktor private machen:

    class Test
    {
    private:
        ~Test(){}
    public:
        Test(){}
        destroy(){delete this;}  
    };
    
    int main()
    {
        Test Hase;
        return 0;
        //Was passiert jetzt???
    }
    

    thx
    Glamdring



  • Hi,

    zu dem Code-Kommentar:
    Höchstwahrscheinlich kommt die Compiler-Fehlermeldung, dass der dtor nicht ausgeführt werden kann, ich habe es nicht ausprobiert, du ja auch nicht.

    Hm, naja, wo macht so eine Klasse Sinn?



  • Da, wo man mit "delete this" arbeiten will/muss. Das geht nämlich nicht, wenn die Objekte auf dem Stack liegen.

    Interessant *merk*



  • class Datenklasse
    {
      int daten;
    }
    
    class Verwalterklasse
    {
      Datenklasse* m_D;
      void Datenhinzufuegen(Datenklasse* D)
      {
         m_D = D;
      }
      void Verwaltedaten()
      {
        if(m_D->Daten == irgendwas)
          blupp;
      }
    }
    
    int main()
    {
      Verwalterklasse V;
      {
        Datenklasse D;
        D.Daten = 10;
        V.Datenhinzufuegen(&D)
      } // <<-- Hier wird D zerstört
      V.Verwaltedaten // <<-- Abrecken
    }
    

    Das ist nur 'ne Art pseudocode, aber wenn "D" (in der main Funktion) zerstört wird und danach in "Verwaltedaten" auf "D" zugegriffen wird, dann dürfte das Programm abrecken.
    Zwingt man den Benutzer stattdessen, die Daten auf dem Heap anzulegen, gibt es keine Probleme.

    EDIT: Kleiner Fehler im code



  • Das ist ja Klasse, sogar ne Compilerfehlermeldung, thx und mfg
    Glamdring



  • Hm, naja, wo macht so eine Klasse Sinn?

    Zum Beispiel wenn das normale delete nicht ganz seinen Zweck erfüllt. Es gibt Fälle an denen man ein Object nicht gelöscht werden kann da es noch im gebrauch ist. Beispile: A ruft B auf, B will A löschen, A muss aber noch etwas fertig machen. Hier will man die Zerstörung von A herauszögern, mit delete geht das aber nicht da man (ohne unschönes hacken) die Speicherfreigabe nicht verhindern kann. Die Lösung ist ganz einfach. A kriegt einen privaten Destruktor, einen Zerstörungsflag, und zwei Funktionen: kill kill_now. Wenn kill aufgerufen wird dann wird gecheckt ob das Object löschbar ist wenn ja wird delete this aufgerufen (wir sind in einer Memberfunktion also kann der private Destruktor aufgerufen werden), fals nicht wird der Zerstörungsflag gesetzt. Wenn A nun fertig ist ruft es kill_now auf, hier wird der Zerstörungsflag gecheckt und fals gesetzt wird delete this aufgerufen.

    Eine Alternative zum privaten Destruktor, die ich bevorzuge, ist der virtuelle protected Destruktor, mit dem Verbaut man sich nicht die Möglichkeit in eine abgeleiteten Klasse ein Destruktor einzubauen (und dieser wird mit der kill Methode auch ganz normal aufgerufen, wie der der Basisklasse).

    Hier das ganz wie ich es benutze:

    class LockObject{
      int lock;
      bool killing;
    protected:
      virtual ~LockObject(){}
    public:
      LockObject():
        lock(0),
        killing(false){}
      void use(){++lock;}
      void free(){
        --lock;
        if(killing&&!lock)
          delete this;
      }
      void kill(){
        --lock;
        if(!lock)
          delete this;
        else
          killing=true;
      }
    };
    class A:public LockObject{
      int*a_ptr;
    public:
      A(int a){
        a_ptr=new int;
        *a_ptr=a;
      }
      ~A(){
        delete a_ptr;
      }
      int get_a(){
        return *a_ptr;
      }
    };
    void foo(A*a){
      a->use();
      a->get_a();
      a->kill();
    }
    void foo2(A*a){
      a->use();
      foo(a);
      a->get_a();
      a->free();
    }
    int main(){
       A*a=new A(5);
       foo2(a);
       return 0;
    }
    

    Hier ist es ganz egal wo der Befehl gegeben wurde a zu zerstören, hauptsache a wird erst zerstört wenn niemand es mehr braucht.



  • Jetzt hab ich aber noch ein Problem:
    Ich habe mir überlegt, dass man eine Basisklasse entwickeln könnte, deren Subklassen auch nur aufm Heap erzeugt werden können. Dazu müsste man aber den Destruktor private und nicht virtuell machen, wodurch man u.U. ein Speicherloch hätte. Wie kann ich eine derartige Basisklasse, ohne Speicherloch machen?

    thx



  • Ok. jetzt hab ich ne Lösung, und die ist so einfach, da hätte man sofort drauf kommen können, hier meine Oberklassen für Objekte, die nur aufm Heap bzw. nur aufm Stack erzeugt werden können:

    template <bool OnlyOnTheHEAP> class Base;	
    
    template <> class Base<true>
    {
    protected:
            virtual void del(){}
    private:
    	~Base(){del();}
    public:
    	Base(){}
    	void destroy(){delete this;}
    };
    
    template <> class Base<false>
    {
    public:
    	Base();
    	virtual ~Base();
    private:
    	static void* operator new(size_t size){}
    	static void operator delete(void* rawmemory){}
    	static void* operator new[](size_t size){}
    	static void operator delete[](void* rawmemory){}
    };
    

    mfg
    Glamdring



  • Hast du's getestet?

    MfG MAV



  • jo, net besonders ausführlich, aber es geht.

    mfg



  • geht doch nicht, wenn ich ne KLase von Base<true> ableite, aber nicht instanziiere gibts dennoch nen Fehler. Das iast doof, aber ich arbeite dran.

    mfg



  • Das ist mein Code:

    template <bool OnlyOnTheHEAP> class Base;   
    
    template <> class Base<true>
    {
    protected:
        virtual void del(){}
    private:
        inline ~Base(){del();}
    public:
        Base(){}
        void destroy(){delete this;}
    };
    
    template <> class Base<false>
    {
    public:
        Base();
        virtual ~Base();
    private:
        static void* operator new(size_t size){}
        static void operator delete(void* rawmemory){}
        static void* operator new[](size_t size){}
        static void operator delete[](void* rawmemory){}
    };
    
    class test : public Base<true>
    {};
    
    int main()
    {
        return 0;
    }
    

    Dieser Code wird von VC++ fehlerfrei compiliert.

    Wenn ich jetzt aber bei test einen Destruktor einfüge, der nichts macht, dann gibts nenn Fehler???

    thx
    Glamdring



  • Hab jetzt nochwas gefunden:

    #include <iostream>
    using namespace std;
    
    template <bool OnlyOnTheHEAP> class Base;   
    
    template <> class Base<true>
    {
    protected:
        virtual void del(){cout << "del von Base" << endl;}
    private:
        ~Base(){del();}
    public:
        Base(){}
        void destroy(){delete this;}
    };
    
    template <> class Base<false>
    {
    public:
        Base();
        virtual ~Base();
    private:
        static void* operator new(size_t size){}
        static void operator delete(void* rawmemory){}
        static void* operator new[](size_t size){}
        static void operator delete[](void* rawmemory){}
    };
    
    class test : public Base<true>
    {
    public:
    	void del(){cout << "del von test" << endl;}
    };
    
    int main()
    {
    	test* user = new test;
    	delete user;
            return 0;
    }
    

    Er compiliert es fehlerfrei, aber am Ende wird nichts ausgegeben.

    ???



  • Auf dem BcB funktioniert das Definitiv nicht

    kann ja auch nicht 😉
    [was hier stand war schrott der eh nur das ausdrückte was jeder weis]



  • läuft auf keinem Compiler, daran arbeite ich gerade

    Ich bin auch schon einen Schritt weiter, ich hab den Destruktor protected aber weiterhin nicht virtuell gemacht, das Problem jetzt: es wird nur del von Base aufgerufen.

    mfg
    Glamdring



  • soweit war ich gestern auch schon.
    wenn du den destruktor in base protectest und an test weitervererbst funktioniert

    test Test;
    

    wieder.
    wenn du aber in test die gleiche struktur wie in base machst, und test dann nochmal vererbst, ja richtig dann muss die abgeleitete klasse von test wieder die struktur haben.
    also sinnlos.
    ich erklär mir das so, dass zuerst der dtor der abgeleiteten klasse aufgerufen wird, der danach den dtor der basisklasse aufruft.
    da der dtor der abgeleiteten Klasse eine methode ist, kann er auf die private elemente und somit auf den vererbten protected dtor der Basisklasse zugreifen.



  • So gehts(auch mitm BCBX):

    template <bool OnlyOnTheHEAP> class Base;
    
    template <> class Base<true>
    {
    protected:
        virtual inline void del(){cout << "del von Base" << endl;}
        inline ~Base(){}
    public:
        Base(){}
        virtual void destroy(){del(); delete this;}
    };
    
    template <> class Base<false>
    {
    public:
        Base();
        virtual ~Base();
    private:
        static void* operator new(size_t size){return 0;}
        static void operator delete(void* rawmemory){}
        static void* operator new[](size_t size){return 0;}
        static void operator delete[](void* rawmemory){}
    };
    

    Dann kann man das hier machen:

    class test : public Base<true>
    {
    public:
        virtual void del(){cout << "del von test" << endl;}
    };
    
    class undertest : public test
    {
    public:
        virtual void del(){cout << "del von undertest" << endl;}
    };
    

    Dann das hier:

    int main()
    {
        undertest* user = new undertest;
        user->destroy();
        return 0;
    }
    

    Und es wird del con undertest ausgegeben.

    thx@all

    mfg



  • haste getestet ob undertest user; funktioniert?

    habs ma fix aufm bcb6.0 getestet:
    undertest user;/funzt->schlecht
    test user;//funzt->schlecht
    Base<true> user//funzt nicht->juhu^^

    //ausserdem is es klar dass del() bei dir ausgegeben wird, immerhin haste es ja als virtual deklariert



  • sry, dann muss ich nochmal ausführlich testen, aber wenn du den Destruktor von Base<true> wieder private machst gehts.

    sry



  • @glamdrink hmm was du meinen? bei base<true> läuft doch alles wie geplant?


Anmelden zum Antworten