Problem mit rekursivem Destruktor



  • Hallo, ich habe ein Problem mit einer Klasse, deren Objekte jeweils eine Liste mit beliebig vielen Objekten der gleichen Klasse enthalten.

    class BoxElement{
        public:
    	~BoxElement();
             bool deleteSubElement(int boxIndex);		
            int getIndex();
    
        protected:
            int index;
    	map<int, class BoxElement*> subElementList;
    };
    

    wenn ein Element aus der Liste entfernt werden soll, muss natürlich das Element mit allen Unterelementen gelöscht werden. Das verläuft rekursiv.

    BoxElement::~BoxElement(){ 
    	map<int, BoxElement*>::iterator f_it = this->subElementList.begin();
    	while(f_it != this->subElementList.end()){
    		this->deleteSubElement(f_it->second->getIndex());
    		++f_it;	
    	}
    }
    
    bool BoxElement::deleteSubElement(int boxIndex){
    	map<int, BoxElement*>::iterator f_it = this->subElementList.find(boxIndex);
    	if(f_it != this->subElementList.end()){
    		this->subElementList.erase(boxIndex); 
    		delete(f_it->second);
    		return(true);
    	}
    	return(false);		
    }
    

    falls nur eine Ebene enthalten ist, funktionieren der Code optimal, allerdings gibt es bei mehreren verschachtelten Ebenen hässliche Fehler während der Laufzeit (ich denke da an Pointer-Fehler, kann aber nichts falsches finden. Kann mir jemand anders vielleicht auf die Sprünge helfen?


  • Mod

    this->subElementList.erase(boxIndex);
            delete(f_it->second);
    

    Da boxIndex auf das gleiche Element verweist wie f_it, wird der Iterator beim erase natürlich ungültig. Es ist eine gute Idee, das delete erst nach dem erase durchzuführen (als allgemeines Prinzip, zudem funktioniert es dann auch, falls du zirkuläre Referenzen hast), aber natürlich musst du dann das Pointerargument vor dem erase sichern. Zudem solltest du erase gleich den Iterator übergeben, sonst muss die map überflüssigerweise zweimal durchsucht werden (dann wäre dir evtl. auch der Fehler aufgefallen).

    auto_ptr<BoxElement> p(f_it->second);
    subElementList.erase(f_it);
    


  • ich weiß nicht ob ich dich richtig verstanden habe.

    die deleteSubElement bekommt immer den Index von dem Objekt geliefert, das aus der Liste entfernt werden soll. Falls überhaupt so ein Objekt existiert werden zuerst alle Referenzen auf dieses Objekt gelöscht (z.B. in der Liste), danach wird das Objekt mit allen Unterobjekten auf die gleiche Weise zerstört.

    Meinst du, dass nach dem Löschen aus der Liste auch der Iterator f_it nicht mehr existiert? Falls ich es so umschreibe, funktioniert ist immer noch nicht.

    bool BoxElement::deleteSubElement(int boxIndex){
        map<int, BoxElement*>::iterator f_it = this->subElementList.find(boxIndex);
        if(f_it != this->subElementList.end()){
            BoxElement* tempP = f_it->second;
            this->subElementList.erase(boxIndex);
            delete(tempP);
            return(true);
        }
        return(false);       
    }
    

    Oder habe ich dich falsch verstanden?

    Das blöde an diesen Pointerfehlern ist, dass Konsolenausgaben an den jeweiligen Stellen im Code teilweise gar nicht mehr angezeigt werden, obwohl das Programm erst danach abstürzt.


  • Mod

    zottl schrieb:

    Meinst du, dass nach dem Löschen aus der Liste auch der Iterator f_it nicht mehr existiert?

    Richtig, es geht sogar noch weiter: auch f_it aus dem aufrufenden Destruktor wird ungültig werden (ich nehme einfach mal an, dass getIndex() den Schlüssel liefert, unter dem das jeweilige Objekt in der Map auftaucht - eine bidirektionale map (boost::multi_index_container) Boost Multi-index Container wäre evtl. eine bessere Lösung). Wenn meine Annahme zutrifft, ist es sowieso unklar, warum den den Umweg über den Index gehst, und nicht direkt den iterator als Argument für deleteSubElement verwendest.

    deleteSubElement((f_it++)->second->getIndex());
    


  • camper schrieb:

    (eine bidirektionale map (boost::multi_index_container) Boost Multi-index Container wäre evtl. eine bessere Lösung)

    wow. mir war bisher nur die STL-Library bekannt. aber maps wie die von boost sind für mich auch sonst viel geschickter. dann werde ich das im gesamten wohl noch mal überarbeiten. mit der lassen sich noch andere sachen viel leichter implementieren. danke!


Anmelden zum Antworten