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

发布于 2024-12-12 09:42:18 字数 1015 浏览 1 评论 0原文

我有一个向量声明为:

vector<Curve> workingSet;

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

我有一个循环应该从向量中删除 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。

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

谁能解释为什么曲线 4 被搞乱了?

注意 (1) 曲线类有一个复制构造函数,它执行“深层”复制。 注意 (2) 类/程序的内容比我在这里提到的还要多。

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

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

I have a vector declared as:

vector<Curve> workingSet;

Curve is a class I've created that contains a string "name" and an array of structs, dynamically allocated by the constructor.

I have a loop that is supposed to delete 2 (out of 4) items from the vector.

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

When the debugger reaches the .erase(it) call I can see that the 'it' iterator is pointing to curve 2 in the vector. This is good; I want curve 2 to be removed from the vector.

I'm then taken by the debugger to the destructor (I have a breakpoint there), which presumably should be destructing curve 2. But when I look at the 'this' watch, I can see that the curve being destructed is curve 4! The destructor then proceeds to 'delete[]' the array in the object, as required and sets the array pointer to NULL.

When the debugger returns to the program, having completed the erase() call, I can see that vector 2 has been removed from the array and curve 4 is still there. Curve 4's array pointer still points to the same location as before, but the memory has been deallocated and the array contains garbage.

Can anyone suggest why curve 4 is being messed with?

N.b. (1) There is a copy constructor for the curve class, which does a 'deep' copy.
N.b. (2) There is more to the class/program than I've mentioned here

Btw, the array pointers in curves 2 and 4 point to different locations and contain different values, according to the debugger.

Edit: I've now implemented copy assignment. Now the correct item seems to be being erased from the vector, but the wrong destructor is still being called! However, when the debugger returns to the array curve 4 is still intact.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一抹微笑 2024-12-19 09:42:18

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

至少应该是这样的。

When an item is erased from the vector, all elements behind it are shifted towards the front to fill the empty space. If your compiler doesn't support move yet, it's done by copying all the elements, and the last item in the vector, which is now copied to the item before it, is a duplicate and is getting deleted.

At least that's how it should work.

深府石板幽径 2024-12-19 09:42:18

在我看来, 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
}

It appears to me that vector::erase cannot be used with a vector of locally constructed non-trivial data types (objects not constructed on the heap, the proper name escapes me right now). I have the exact same behavior you describe, the last element gets destructed twice (especially dangerous if your object has memory that is freed by the destructor) and the element that you removed is never destructed. I do not know why this happens, but it is a pitfall to watch out for.

Here is one way to fix the problem:

#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
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文