矢量擦除功能删除错误的对象

时间:2011-10-30 16:19:09

标签: c++ vector stdvector

我有一个声明为:

的向量
vector<Curve> workingSet;

Curve是我创建的一个类,它包含一个字符串“name”和一个结构数组,由构造函数动态分配。

我有一个循环,它应该从向量中删除2个(中的4个)项目。

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;
        }

当调试器到达.erase(it)调用时,我可以看到'it'迭代器指向向量中的曲线2。这很好;我希望从矢量中删除曲线2。

然后我被调试器带到了析构函数(我有一个断点),这可能是破坏曲线2.但是当我看到'this'表时,我可以看到被破坏的曲线是曲线4!然后,析构函数根据需要继续“删除[]”对象中的数组,并将数组指针设置为NULL。

当调试器返回程序时,完成erase()调用后,我可以看到向量2已从数组中删除,曲线4仍然存在。曲线4的数组指针仍指向与之前相同的位置,但内存已被释放,数组中包含垃圾。

任何人都可以建议为什么曲线4被搞乱?

N.b。 (1)曲线类有一个复制构造函数,它执行“深度”复制。 注: (2)课程/课程比我在这里提到的更多

顺便说一下,根据调试器,曲线2和4中的数组指针指向不同的位置并包含不同的值。

编辑:我现在已经实现了复制分配。现在正确的项目似乎正在从向量中删除,但仍然会调用错误的析构函数!但是,当调试器返回到数组时,曲线4仍然完好无损。

2 个答案:

答案 0 :(得分:2)

当一个项目从矢量中删除时,它后面的所有元素都会向前移动以填充空白区域。如果您的编译器还不支持move,则通过复制所有元素来完成,并且向量中的最后一项(现在复制到它之前的项目)是重复的并且正在被删除。

至少应该如何运作。

答案 1 :(得分:0)

在我看来,vector :: erase不能与本地构造的非平凡数据类型的向量一起使用(未在堆上构造的对象,正确的名称现在让我逃避)。我有与你描述的完全相同的行为,最后一个元素被破坏两次(如果你的对象具有被析构函数释放的内存,则特别危险)并且你删除的元素永远不会被破坏。我不知道为什么会这样,但要注意这是一个陷阱。

以下是解决问题的一种方法:

#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
}