我有一个声明为:
的向量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仍然完好无损。
答案 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
}