delete von klassenvariablen



  • Hallo,

    bisher habe ich ein delete so gemacht:

    //Aufruf zum delete
    
    Hash_Table<T>* ht = NULL;
    //Mache was mit ht - fülle es z.B
    
    ht->Delete_Hash_Table(ht);
    
    //jetzt die delete methode
    template<class T>	void
    Hash_Table<T>::Delete_Hash_Table(Hash_Table<T> *ht)
    {
    	if (ht) 
    	{
    		for (int i = 0; i < ht->size; i++) 
    		{
    			if (ht->col_idcs_table[i]) 
    				delete [] ht->col_idcs_table[i];
    			if (ht->row_idcs_table[i]) 
    				delete [] ht->row_idcs_table[i];
    			if (ht->vals_table[i]) 
    				delete [] ht->vals_table[i];
    		}
    		delete [] ht->col_idcs_table;
    		delete [] ht->row_idcs_table;
    		delete [] ht->vals_table;
    		delete ht;
    	}
    }
    

    Ich meine aber, dass es da es member sind auch so geht:

    Hash_Table<T>* ht = NULL;
    //Mache was mit ht - fülle es z.B
    
    ht->Delete_Hash_Table();
    
    template<class T>	void
    Hash_Table<T>::Delete_Hash_Table()
    {
    	if (this) 
    	{
    		for (int i = 0; i < size; i++) 
    		{
    			if (col_idcs_table[i]) 
    				delete [] col_idcs_table[i];
    			if (row_idcs_table[i]) 
    				delete [] row_idcs_table[i];
    			if (vals_table[i]) 
    				delete [] vals_table[i];
    		}
    		delete [] col_idcs_table;
    		delete [] row_idcs_table;
    		delete [] vals_table;
    		delete this;
    	}
    }
    

    Mir geht es jetzt darum: Ist das if(this) hier korrekt? Also wäre das auch ein pointer?
    Ist denn über den Aufruf ht->Delete_Hash_Table(); klar, dass die member von diesem ht und nicht z.B eines anderen ht2 gelöscht werden?
    wäre das so in Ordnung?

    Danke



  • gast_xy schrieb:

    Mir geht es jetzt darum: Ist das if(this) hier korrekt? Also wäre das auch ein pointer?

    Ich würde eher ein assert nehmen, wenn überhaupt 🙂 Und ja, der this-Zeiger kann auch NULL sein, etwa hier:

    Hash_Table<T>* ht = NULL;
    ht->Delete_Hash_Table();
    

    gast_xy schrieb:

    Ist denn über den Aufruf ht->Delete_Hash_Table(); klar, dass die member von diesem ht und nicht z.B eines anderen ht2 gelöscht werden?
    wäre das so in Ordnung?

    Ich denke du solltest es eher über einen Destruktor machen, die gibts ja nicht umsonst. Wird z.B. das Hash-Table-Objekt auf dem Stack angelegt Hash_Table<T> ht; , dann schlägt delete this; natürlich fehl! Von daher: Die ganzen delete-Sachen in den Destruktor packen, dann muss aber ein mit new erzeugtes Hash-Table auch mit delete wieder zerstört werden.



  • hmmm...ok.
    Das heißt aber doch ihc muss dann den destruktor auch aufrufen nicht wahr?

    ht->~Delete_Hash_Table();
    

    Prinzipiell ging es mir hauptsächlich darum, ob so auch korrekt auf die Membervariablen zugegriffen wird. Also ich wollte wissen ob ich denn das *ht überhaupt übergeben sollte oder ob das sowieso aus dem aufruf ht->fct() klar hervorgeht.
    Mit anderen worten:

    Hash_Table<T>* ht1, *ht2;
    
    ht1->fct(); //verändert doch nur die member von OBjekt ht1 oder?
    ht2->fct(); //verändert doch nicht die member von ht1, sondern seine eigenen oder?
    

    also der pointer-aufruf ist eindeutig oder?

    Weitere frage:

    folgendes:

    Hash_Table<T> ht = Hash_Table<T>();
    
    ht.~Delete_Hash_Table();
    
    // wird hier trotzdem in den if-zweig des delete geprungen? Ja oder, ein statisches Objekt ist ungleich NULL oder?
    


  • Ich glaube du machst dir da zuviele Gedanken 😉
    Wenn du zwei Instanzen von Hash_Table hast, hat natürlich jedes davon seine eigenen Variablen und deren Funktionen greifen dann natürlich auch nur auf die Variablen des jeweiligen Objektes zu.

    Und wegen Kon- und Destruktor-Aufrufen: Das geht auch alles von alleine. Du hast zwei Möglichkeiten, du kannst Objekte auf dem Stack oder auf dem Heap ablegen.
    Stack:

    void SomeFunction()
    {
        Hash_Table<int> ht(); // Hier wird der Konstruktor aufgerufen, () kannst du auch weglassen
        ht.BlaBla();
        // Hier ist der "Scope" vorbei, das Objekt ht wird zerstört, der Destruktor wird automatisch aufgerufen
    }
    

    Heap:

    void SomeFunction()
    {
        Hash_Table<int>* ht = new Hash_Table<int>(); // Hier wird Speicher angefordert und der Konstruktor aufgerufen
        // ht ist hier nur ein Zeiger auf das Objekt, nicht das Objekt selber
        // Das bedeutet, am Ende vom Block/Scope wird nicht automatisch der Destruktor aufgerufen, also:
        delete ht; // Destruktor wird aufgerufen und der Speicher wieder freigegeben
    }
    

    Der Vorteil, Variablen auf dem Heap freizugeben, ist, dass sie eben nicht beim Verlassen des Blocks kaputtgemacht werden. D.h. du kannst den Zeiger rumreichen und das Objekt dahinter erstellen und zerstören wann du willst 🙂

    Achso, und deine Klasse könnte dann so aussehen:

    template<class T> class Hash_Table
    {
        public:
            Hash_Table() // Konstruktor
            {
                // Hier kannst du alle deine Member-Variablen erstellen, z.B.
                col_idcs_table = new T[100];
            }
    
            ~Hash_Table()
            {
                for (int i = 0; i < size; i++)
                {
                    if (col_idcs_table[i])
                        delete [] col_idcs_table[i];
                    if (row_idcs_table[i])
                        delete [] row_idcs_table[i];
                    if (vals_table[i])
                        delete [] vals_table[i];
                }
                delete [] col_idcs_table;
                delete [] row_idcs_table;
                delete [] vals_table;
                //delete this; das brauchst du jetzt nicht mehr :)
            }
    
        private:
            T* col_idcs_table;
            T* row_idcs_table;
    };
    


  • Vielen Dank 🙂
    Hat mir sehr geholfen!!!



  • Badestrand schrieb:

    Ich würde eher ein assert nehmen, wenn überhaupt 🙂 Und ja, der this-Zeiger kann auch NULL sein, etwa hier:

    Hash_Table<T>* ht = NULL;
    ht->Delete_Hash_Table();
    

    Der Aufruf erzeugt schon undefiniertes Verhalten. Du landest zwar in der Praxis bei nicht-virtuellen Funktionen tatsächlich in der Methode mit this==0, aber toller Stil ist das nicht gerade. Das assert gehört VOR den Aufruf der Methode.



  • Kann ihc denn an einen destruktor auch parameter übergeben:
    ~Destructor(int num); ?



  • gast_xy schrieb:

    Kann ihc denn an einen destruktor auch parameter übergeben:
    ~Destructor(int num); ?

    ne



  • Warum geht denn das folgende nicht ohne speicherleck:

    Index_Set *a = NULL;
    
    delete a;
    
    Index_Set::Index_Set(int len_a)
    {
    	idcs = NULL;
    	len = len_a;
    	idcs = new int[len];
    }
    
    Index_Set::~Index_Set()
    
    {
            if(idcs)
    	    delete [] idcs;
    }
    
    // idcs sind als member : int * idcs;
    


  • Ehm

    Index_Set *a = NULL;
    delete a;
    

    Löschen eines Null-Zeigers. Kein Problem 🙂 Darfst a nur nicht dazwischen nutzen!

    Index_Set::Index_Set(std::size_t len) : m_len(len)
    {
        m_idcs = new int[len];
    }
    

    geht auch 🙂 len muss aber positive Ganzzahl sein. D.h. std::size_t ...

    Index_Set::~Index_Set()
    {
        delete [] m_idcs;
    }
    

    NULL-Zeiger führen nicht zu undefiniertem Verhalten. Das ist auch i.O. 🙂
    =>Sollte d.h. so gehen 🙂



  • gast_xy schrieb:

    Warum geht denn das folgende nicht ohne speicherleck:

    In dem Codestück passiert garnichts... Wo siehst Du ein Speicherleck?



  • Kann es sein, dass das am pointer liegt?
    so lösche ich doch nur den pointer, nicht aber die member oder? Natürlich sind die Member gefüllt vorher. Wie lösche ich denn die member von Index_Set ohne lecks wenn ich sowas habe?

    Index_Set* a = NULL;
    
    Index_Set* b = NULL;
    
    b = new Index_Set(5);
    b->idcs[0] = 0;
    //fülle b weiter
    
    a = b;
    
    //hier verursacht ein delete a Probleme? Evtl. weil nur der Pointer gelöscht wird?
    delete a;
    


  • Du löscht niemals Pointer. Du löscht immer ein Objekt, dessen Adresse in einem Pointer gespeichert ist. Wenn das Objekt gelöscht wird, werden natürlich der Destruktor und die Destruktoren aller Mitglieder des Objekts auch aufgerufen.

    Wenn Du Zeiger im Objekt hast (wie idcs), musst Du diese selbst verwalten, sprich löschen wie in Deinem vorletzten Beispiel mit delete im Destruktor. Damit sollte Dein Code keinerlei Probleme machen.



  • Index_Set* a = NULL; // a zeigt nirgendwo hin (0)
    Index_Set* b = new Index_Set(5); // b zeigt auf ein Index_Set-Objekt auf dem Heap
    a = b; // a zeigt auf das Index_Set-Objekt auf das auch b zeigt.
    delete a; // Das Index_Set-Objekt auf das a und b zeigen wird gelöscht. ({ ~dtor })
    // !!! ACHTUNG !!!
    // b zeigt auf den nicht mehr existierenden Bereich aufm Heap ...
    


  • Also langsam verzweifle ich:

    bevor ich etwas ändere, habe ich wirklich ein speicherleckfreies programm.

    Sobals ich NUR diesen destruktor reinbaue OHNE dass er aufgerufen wird. Es wird immer noch Index_Set::Delete_Index_Set(Index_Set* is) aufgerufen!!!
    Aber so bekomme ich 8000 errors:

    Index_Set::~Index_Set()
    
    {
            if (idcs)
    	     delete [] idcs;
    }
    

    Ich initialisiere meine Index_Set pointer nur so:

    Index_Set * a = NULL;
    a = new Index_Set(...);
    
    //Mein Konstruktor:
    Index_Set::Index_Set(int len_a)
    {
    	idcs = NULL;
    	len = len_a;
    	idcs = new int[len];
    }
    

    das kann doch nicht sein 😕



  • er schreit mit:

    Conditional jump or move depends on uninitialised value(s)
    ==9597==    at 0x8052FAA: Index_Set::~Index_Set() ....in..
    


  • ok es liegt an folgender sache:

    Index_Set** j_sets;
    
    //j_sets wird initialisiert
    
    Index_Set *a = NULL;
    a = new Index_Set(3);
    j_sets[0] = a;
    
    Index_Set *b = NULL;
    b = j_sets[0];
    
    //das folgende delete geht nicht mehr ohne probleme - WARUM?
    delete b;
    


  • Ehm sollte vorher schon raushauen ^^

    Index_Set** ptr_array_set = new Index_Set*[1]; // !!! Muss schon auf was zeigen ;) !!!
    
    Index_Set* ptr_a = new Index_Set(3);
    ptr_array_set[0] = ptr_a;
    
    Index_Set* ptr_b = ptr_array_set[0];
    delete ptr_b;
    ptr_b = NULL; // s.u.
    ptr_a = NULL; // damit man nicht mehr in Versuchung kommt ptr_a zu nutzen :P
    delete [] ptr_array_set; // muss auch wieder freigegeben werden!
    


  • Ich bin schon ganz balabala...

    warum geht dann folgendes nicht und wie kann ich es lösen?:

    Index_Set * a = NULL;
    
    for ( int i = 0; i < 5; i++)
    {
       //Get_Index_Set funktioniert !!!!
       a = Get_Index_Set(...);
    
       //mache was mit a
    
       if (//some_condition)
            break;
    
       delete a;
    }
    //hier muss das delete weg - aber wenn die break anweisung kommt 
    //kann ich nicht einfach hier das delete hinmachen anstatt vor die break-anweisung?
    delete a;
    


  • Der Teil ist korrekt ... Get_Index_Set aber nicht

    Index_Set * a = NULL;
    for (std::size_t i = 0; i < 5; ++i)
    {
       a = get_index_set(...);
       if ( ... )
            break;
    
       delete a;
    }
    
    delete a; // delete NULL ist definiert.
    

    ...


Anmelden zum Antworten