2011-10-30 3 views
1

J'ai un vecteur déclaré:fonction d'effacement de vecteur suppression de mauvais objet

vector<Curve> workingSet; 

Curve est une classe que j'ai créé qui contient une chaîne « nom » et un tableau de struct, allouée dynamiquement par le constructeur.

J'ai une boucle censée supprimer 2 (sur 4) éléments du vecteur.

vector<Curve>::iterator it = workingSet.begin(); 

    //remove tbi's children from the working set 
    for ( ; it != workingSet.end();){ 
     if (it->thisName == tbi.childAName ||it->thisName == tbi.childBName) 
      it= workingSet.erase(it); 
     else 
      ++it; 
     } 

Lorsque le débogueur atteint le .erase (it) appeler Je peux voir que le « it » itérateur pointe sur la courbe 2 dans le vecteur. C'est bon; Je veux que la courbe 2 soit retirée du vecteur.

Je suis alors amené par le débogueur vers le destructeur (j'ai un point d'arrêt là), ce qui devrait probablement détruire la courbe 2. Mais quand je regarde la montre 'this', je peux voir que la courbe est détruite est la courbe 4! Le destructeur procède alors à 'delete []' le tableau dans l'objet, comme requis et définit le pointeur de tableau à NULL.

Lorsque le débogueur revient au programme, après avoir terminé l'appel erase(), je peux voir que le vecteur 2 a été retiré du tableau et la courbe 4 est toujours là. Le pointeur de tableau de Curve 4 pointe toujours vers le même emplacement que précédemment, mais la mémoire a été désallouée et le tableau contient des données parasites.

Quelqu'un peut-il suggérer pourquoi la courbe 4 est en train d'être foiré?

N.b. (1) Il existe un constructeur de copie pour la classe de courbe, qui effectue une copie 'profonde'. N.b. (2) Il y a plus à la classe/programme que je l'ai mentionné ici

Btw, les pointeurs de tableau dans les courbes 2 et 4 pointent vers des emplacements différents et contiennent des valeurs différentes, selon le débogueur.

Modifier: J'ai maintenant implémenté l'affectation de copie. Maintenant, l'élément correct semble être effacé du vecteur, mais le mauvais destructeur est encore appelé! Cependant, lorsque le débogueur retourne à la courbe de tableau 4 est toujours intacte.

+2

Pouvez-vous fournir un échantillon minimal compilable qui montre le problème? Votre description verbale n'est pas très utile. Quoi qu'il en soit: Jetez un oeil à l'Erase/Remove-Idiom. Nous avons des tonnes de questions qui sont répondues de cette façon sur SO. – pmr

+2

Autre chose: vous ne mentionnez qu'un copieur, avez-vous également mis en place une affectation de copie? – pmr

+0

Je n'ai pas mis en place d'affectation de copie, je vais le faire maintenant et je reviendrai vers vous. – Meir

Répondre

2

Lorsqu'un élément est effacé du vecteur, tous les éléments situés derrière lui sont décalés vers l'avant pour remplir l'espace vide. Si votre compilateur ne supporte pas encore move, il le fait en copiant tous les éléments, et le dernier élément du vecteur, qui est maintenant copié sur l'élément précédent, est un doublon et est supprimé.

Au moins, c'est ainsi que cela devrait fonctionner.

0

Il me semble que vector :: erase ne peut pas être utilisé avec un vecteur de types de données non triviaux construits localement (objets non construits sur le tas, le nom propre m'échappe actuellement). J'ai exactement le même comportement que vous décrivez, le dernier élément est détruit deux fois (particulièrement dangereux si votre objet a de la mémoire qui est libérée par le destructeur) et l'élément que vous avez retiré n'est jamais détruit. Je ne sais pas pourquoi cela arrive, mais c'est un piège à surveiller.

Voici une façon de résoudre le problème:

#include <iostream> 
#include <vector> 
#include <memory> 

using namespace std; 

class MyClass 
{ 
    public: 
    int *x; 
    MyClass(int x) 
    { 
     cout << "MyClass Constructor " << x << endl; 
     this->x = new int(x); 
    } 
    MyClass(const MyClass& obj) 
    { 
     this->x = new int(*obj.x); 
     cout << "copy constructor " << *this->x << endl; 
    } 
    ~MyClass() 
    { 
     cout << "MyClass Destructor " << *x << endl; 
     delete x; 
    } 
}; 
int main(int argc, char* argv[]) 
{ 
    // incorrect 
    vector<MyClass> bad_vect; 
    for(int i=0;i<3;i++){ 
     bad_vect.push_back(MyClass(i)); 
     // causes a bunch of copying to happen. 
     // std::move does not seem to fix this either 
     // but in the end everything gets constructed as we'd like 
    } 
    cout << " ---- " << endl; 
    bad_vect.erase(bad_vect.begin() + 1); // we expect this to remove item with x = 1 and destruct it. 
    // but it does NOT do that, it does remove the item with x=1 from the vector 
    // but it destructs the last item in the vector, with x=2, clearly 
    // not what we want. The destructor for object with x=1 never gets called 
    // and the destructor for the last item gets called twice. 
    // The first time with x=2 and since that memory is freed, the 2nd time 
    // it prints garbage. Strangely the double-free doesn't crash the prog 
    // but I wouldn't count on that all the time. 
    // Seems some parts of STL have pitfalls with locally constructed objects 
    cout << " ------------ " << endl; 
    // below fixes this 
    vector<unique_ptr<MyClass> >vect; 
    for(int i=0;i<3;i++){ 
     unique_ptr<MyClass> ptr(new MyClass(i)); 
     vect.push_back(move(ptr)); // move is required since unique_ptr can only have one owner 
     // or the single one-liner below 
     //vect.push_back(move(unique_ptr<MyClass>(new MyClass(i)))); 
    } 
    // the above prints out MyClass Constructor 0,1,2, etc 
    vect.erase(vect.begin() + 1); // remove the 2nd element, ie with x=1 
    // the above prints out MyClass Destructor 1, which is what we want 

    for(auto& v : vect){ 
    cout << *(v->x) << endl; 
    } // prints out 0 and 2 
    return 0; // since we're using smart pointers, the destructors for the 
      // remaining 2 objects are called. You could use regular pointers 
      // but you would have to manually delete everything. shared_ptr 
      // also works and you don't need the move(), but with a bit more overhead 
} 
Questions connexes