2011-01-13 21 views
2

Ich habe ein wirklich seltsames Problem mit STL-Vektoren, in denen der falsche Destruktor für das richtige Objekt aufgerufen wird, wenn ich die Löschmethode aufrufen, wenn das Sinn macht.
Mein Code sieht wie folgt aus etwas:
Seltsames Problem mit Vektoren

for(vector<Category>::iterator iter = this->children.begin(); iter != this->children.end(); iter++) 
    { 
     if((*iter).item == item) 
     { 
      this->children.erase(iter); 
      return; 
     } 
     ------------------------- 
    } 

Es ist nur eine einfache Funktion, die das Element in dem Vektor findet, die einen Gegenstand muss gesucht werden, und aus dem Vektorelement, das entfernt werden. Mein Problem ist, als wenn die Löschfunktion aufgerufen wird und somit das Objekt, auf das der Iterator zeigt, zerstört wird, wird der falsche Destruktor aufgerufen. Genauer gesagt wird der Destruktor des letzten Elements im Vektor aufgerufen und nicht das tatsächliche Objekt, das entfernt wird. So wird der Speicher vom falschen Objekt entfernt, das immer noch ein Element im Vektor sein wird, und das tatsächliche Objekt, das aus dem Vektor entfernt wird, hat immer noch seinen gesamten Speicher intakt.
Die costructor des Objekts sieht wie folgt aus:

Category::Category(const Category &from) 
{ 
    this->name = from.name; 
    for(vector<Category>::const_iterator iter = from.children.begin(); iter != from.children.end(); iter++) 
     this->children.push_back((*iter)); 

    this->item = new QTreeWidgetItem; 
} 

Und die destructor

Category::~Category() 
{ 
    this->children.clear(); 
    if(this->item != NULL) 
    { 
     QTreeWidgetItem* parent = this->item->parent(); 
     if(parent != NULL) parent->removeChild(this->item); 
     delete this->item; 
    } 
} 
+1

Wie sieht Ihr Kopierzuweisungsoperator aus? –

+0

Ein funktionierendes minimales Beispiel wäre so viel willkommener. – karlphillip

+0

Ich habe keinen Copy Assigment Operator, ich benutze ihn nicht wirklich. –

Antwort

5

Wenn Sie Ihr Element aus dem Vektor löschen, wird jedes Element nach dem Kopieren (mit dem Zuweisungsoperator) an die vorherige Stelle im Vektor kopiert. Sobald dies abgeschlossen ist, wird das letzte Element im Vektor zerstört. Dies könnte der Grund sein, warum dein letztes Element zerstört wird. Regel Nummer eins bei der Verwendung der STL ist sicherzustellen, dass die Kopiesemantik für Ihr Objekt korrekt ist.

sollten Sie erwägen einen Zuweisungsoperator schreiben:

Category & operator =(const Category & other); 

Obwohl dies nicht so einfach sein kann, wie es klingt, wenn man bedenkt werden Objekte und viele Male in den Vektor destructed kopiert werden.

+0

auslöste. Ja tatsächlich hatte ich keine Idee, dass so std :: vector funktioniert. Ich habe den Zuweisungsoperator erstellt und er wird tatsächlich vor dem Destruktor aufgerufen. Ich denke, ich sollte zu einem Vektor von Zeigern anstatt von Objekten wechseln. –

+0

Seien Sie vorsichtig, Sie haben einen rohen Zeiger, und Sie müssen die Auswirkungen der Kopiervorgänge auf diesen Zeiger verstehen, insbesondere in Anbetracht Ihres Destruktors. Denken Sie daran, der Vektor kopiert einen nach dem Löschen und zerstört das Original, in Ihrem aktuellen Code wird der Zeiger gelöscht (ich nehme an, dass Sie kein neues Widget auf der Kopie erstellen können, da dies ein UI-Element ist ?) und jetzt hält dein Element im Vektor etwas fest, das gelöscht wurde ... – Nim

0

Sie wahrscheinlich Standard-Algorithmen verwenden sollten.

Das Hauptproblem, das ich sehe, ist, dass Ihr Destruktor für Kategorie seinen übergeordneten Vektor anfordert, es zu entfernen. Das kann nicht stimmen, der Destruktor passiert nur, wenn der Vektor ihn bereits entfernt.

std :: vector verwendet placement-new, so dass Ihr Destruktor direkt aufgerufen wird. Ich weiß nicht, welche Wirkung es hat, an diesem Punkt zum Vektor zurückzukehren.

Entfernen Sie die Zeile if (parent != NULL) parent->removeChild(this->item) aus dem Destruktor. Es ist nicht was du willst.

+0

Ich musste diese Zeile auch tun, aber tatsächlich das entfernt ein Widget von einem anderen - was vollkommen akzeptabel ist ... – Nim

+0

@Nim Das Problem ist, dass das Entfernen eines Elements aus einem Vektor automatisch seinen Konstruktor aufrufen wird (Sie haben einen Vektor von Objekten, nicht von Zeigern. Daher werden die Objekte zerstört). Natürlich, wenn Sie "in der Mitte" löschen, tut es tatsächlich eine Ladung von Kopien, aber die Folge wird die gleiche sein, das Objekt mit Ihrem Artikel wird entfernt. Und dann im Destruktor gehst du zurück zum Vektor und fragst ihn, den Gegenstand zu entfernen. – CashCow

+0

'removeChild()' ist Teil der * Qt * -Schnittstelle, ich glaube nicht, dass es die Methode ist, die das Löschen aus dem Vektor auslöst. – Nim

0

Dies ist das erwartete Verhalten. Auf meiner Implementierung (und ich vermute, auf Ihrer), wenn ein Element aus einem Vektor gelöscht wird, werden Elemente von n + 1 bis zum Ende zugewiesen und das allerletzte Element wird zerstört.

Verwenden Sie std::list, wenn Sie dies nicht möchten.

Demo:

#include <iostream> 
#include <vector> 
struct Category 
{ 
     int item; 
     Category(int n=0) : item(n) {} 
     ~Category() { std::cout << "Category " << item << " destroyed\n"; } 
}; 
int main() 
{ 
     std::vector<Category> children(3); 
     children[0] = Category(0); 
     children[1] = Category(1); 
     children[2] = Category(2); 

     int item = 0; 
     std::cout << " beginning the loop \n"; 
     for(std::vector<Category>::iterator iter = children.begin(); 
             iter != children.end(); ++iter) 
     { 
      if(iter->item == item) 
      { 
        children.erase(iter); // prints "Category 2 destroyed"! 
        break; 
      } 
     } 
     std::cout << " loop done \n"; 
} // this will print "Category 1 destroyed" and "Category 2 destroyed" 

Und ja, explizite erase/remove_if ist besser lesbar als die Schleife.