简体   繁体   English

间歇性的“向量迭代器不可引用”

[英]Intermittent “Vector iterator not dereferencable”

I have the following code, but when I run it, I intermittently get a Debug Assertion Failed error, with the reason "vector iterator not dereferencable". 我有以下代码,但是当我运行它时,我间歇性地收到“调试断言失败”错误,其原因是“向量迭代器不可取消”。

What I'm trying to do with the program is create a bunch of 2D shapes (squares and circles), then move them about one by one, checking for collisions as they go. 我要对该程序进行的操作是创建一堆2D形状(正方形和圆形),然后将它们一个接一个地移动,检查碰撞的过程。 When two shapes collide, I want them to be removed, until there is (at most) one shape remaining. 当两个形状发生碰撞时,我希望将它们删除,直到(最多)剩下一个形状。 About 1 in 3 attempts results in a successful outcome, where the program finds four collisions, then stops. 大约每3次尝试中就有1次导致成功的结果,程序会发现四个冲突,然后停止。 Other times, it will find 1-3 collisions and then error. 其他时候,它将发现1-3次碰撞,然后出错。

I should also mention that I'm relatively new to C++ (and yes, this is a coursework assignment), so if there are any other rookie mistakes in my code, please do point them out! 我还应该提到我对C ++比较陌生(是的,这是一个课程作业),因此,如果我的代码中还有其他菜鸟错误,请指出这些错误!

My methods for moving and colliding seem fine on their own, so I've not included them here. 我自己的移动和碰撞方法看起来不错,因此这里未包括它们。

Square * s1 = new Square(seedCoord(limit), seedCoord(limit), 2.0);
... //There is 9 new shapes created here (squares and circles)

std::vector<Shape*> shape;
shape.push_back(s1);
... //All 9 shapes added to the vector

int m = shape.size();
bool collision = false;

while(m>1) //Keep running until only one shape hasn't collided
{       
    for (auto i=shape.begin(); i!=(shape.end());) //Perform move on each shape in sequence
    {   
        collision = false; //Set collision boolean to false

        (*i)->move(seedCoord(0.5), direction[(rand() % 4)], limit); //Perform move on shape

        for (auto j=shape.begin(); j!=(shape.end()-1);) //Check for collision with each shape in sequence
        {
            if((*i) != (*j)) //Ignore self when checking for collision
            {
                if((*i)->collide(*j)) //Check for collision
                {
                    std::cout << "Collision between " << (*i) << " and " << (*j) << "\n";
                    m=m-2; //Lower shapes count by two (ie remove two collided shapes)
                    delete *i; //Delete pointer to initial shape
                    i = shape.erase(i); //Erase shape object
                    delete *j; //Delete pointer to collided shape
                    j = shape.erase(j); //Erase shape object
                    collision = true; //Set collision boolean to true
                    break; //Break out of internal for-loop (shape has been deleted so no more checks are necessary)
                }
                else
                {
                    j++; //If no collision, move onto next shape
                }
            }
            else
            {
                j++; //If i=j, move onto next shape
            }
        }
        if(!collision) //If no collision with any shape, move onto next shape
        {
            i++; //If no collision with any shape, move onto next shape
        }
        else
        {
            break; //If collision has occurred, break out of external for-loop (shape has been deleted so no more checks are necessary)
        }
    }
}

Quoting cpprefernce on erase : 在擦除时引用cpprefernce

Iterators and references to the erased elements and to the elements between them and the end of the container are invalidated. 迭代器和对已删除元素的引用以及对它们与容器末尾之间的元素的引用均无效。 The past-the-end iterator is also invalidated. 过去的迭代器也无效。

My guess is that your problem occurs when in your second call to erase(j) , j is an iterator referring to a location between i and shape.end() and got invalidated. 我的猜测是,当您在第二次调用erase(j) ,出现了您的问题, j是一个迭代器,该迭代器引用了ishape.end()之间的一个位置,并shape.end()失效。

You are running with two iterators over the same vector. 您正在同一个向量上运行两个迭代器。 When you erase an element of that vector, all iterators past that element are invalidated. 擦除该向量的元素时,该元素之后的所有迭代器都会失效。 That means that on each collision either i or j is invalidated as erase is called for the other one. 这意味着,在每次碰撞时, ij都将无效,因为调用另一个会导致erase

You continue to use the iterators after the erase. 擦除后,您将继续使用迭代器。 This gives undefined behavior, meaning "anything can happen". 这给出了不确定的行为,表示“可能发生任何事情”。 That "aything" means it can work most of the times, but there are times when it won't. “没事”意味着它在大多数情况下都可以工作,但有时却不行。 Consider this case: 考虑这种情况:

The last two elements collide, i is end()-2 , j is end()-1 . 最后两个元素发生碰撞, iend()-2jend()-1 First you erase(i) , meaning all elements get shoved one to the left, including the one j pointed to. 首先,您要erase(i) ,这意味着所有元素都将向左推一个,包括指向j的一个。 j is now conceptually invalid, but being not much more than a pointer, it points now one past the last element, ie j == end() . j现在在概念上是无效的,但仅是一个指针,它现在指向最后一个元素之后的一个,即j == end() You now go on and call delete *j; 您现在继续并调用delete *j; , dereferencing the end() iterator wich will trigger the assertion. ,取消引用end()迭代器将触发该断言。

Looking at the code more closely, it looks like your issue is due to the erasure of elements 更仔细地查看代码,看来您的问题是由于元素擦除引起的

Namely 亦即

i = shape.erase(i); //Erase shape object
...
j = shape.erase(j); //Erase shape object

Now you're correctly using the newly returned iterator from vector.erase() but the problem is that i and j are both iterators into the same vector. 现在您正确地使用了vector.erase()中新返回的迭代器,但是问题是i和j都是同一向量中的迭代器。 This means the following happens 这意味着发生以下情况

  • 'i' is erased from the shape vector 从形状向量中删除“ i”
  • shape vector gets reallocated without the 'i' element 形状向量在没有'i'元素的情况下重新分配
  • 'i' is reassigned to the next element, within the vector's memory block (which has had the remaining elements after i shuffled down a position) “ i”被重新分配给向量的内存块中的下一个元素(在i排列位置后,剩余的元素已被保留)
  • 'j' is still pointing at the old memory location (not taking into account the shuffling) 'j'仍指向旧的内存位置(不考虑改组)
  • 'j' is erased from shape vector (which is potentially now after the last element) 从形状矢量中删除了“ j”(现在可能在最后一个元素之后)
  • out of bounds error 越界错误

Instead of deleting the elements within the loop, you'd be better off flagging your elements as 'dead' within the body of the loop, then using std::remove_if right at the end of the while loop 与其删除循环中的元素,不如将其标记为循环体内的“死”元素,然后在while循环结束时使用std::remove_if更好。

shape.erase(std::remove_if(shape.begin(), 
                           shape.end(),
                           isDead),
                           shape.end());

where isDead is a functor defined something like: 其中isDead是一个isDead 函数,定义如下:

struct isDead 
{
    bool operator()(const Shape* pX) const 
    {
        return pX->isDead();
    }
};

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM