间歇性“矢量迭代器不可解除”

时间:2013-11-22 15:50:49

标签: c++ vector stl iterator

我有以下代码,但是当我运行它时,我间歇性地得到一个Debug Assertion Failed错误,原因是“vector iterator not dereferencable”。

我正在尝试使用该程序创建一堆2D形状(正方形和圆形),然后逐个移动它们,检查碰撞情况。当两个形状碰撞时,我希望它们被移除,直到(最多)剩下一个形状。大约三分之一的尝试导致成功的结果,程序发现四次碰撞,然后停止。其他时候,它会发现1-3次碰撞然后发生错误。

我还应该提一下,我对C ++比较陌生(是的,这是一个课程作业),所以如果我的代码中有任何其他新手错误,请指出它们!

我的移动和碰撞方法看起来很好,所以我没有将它们包含在这里。

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

3 个答案:

答案 0 :(得分:2)

引用cpprefernce on erase

  

对擦除元素以及它们与容器末尾之间的元素的迭代器和引用无效。过去的迭代器也会失效。

我的猜测是,在您第二次致电erase(j)时出现问题,j是一个迭代器,指的是ishape.end()之间的位置并且无效。

答案 1 :(得分:1)

您在同一向量上运行两个迭代器。擦除该向量的元素时,超过该元素的所有迭代器都将失效。这意味着在每次碰撞时,ij都会失效,因为另一个碰撞会调用erase

擦除后继续使用迭代器。这给出了未定义的行为,意思是“任何事情都可能发生”。 “aything”意味着它可以在大多数时间工作,但有时它不会。考虑这种情况:

最后两个元素发生碰撞,iend()-2jend()-1。首先你erase(i),意思是所有元素都被推到左边,包括指向的jj现在在概念上是无效的,但不仅仅是一个指针,它现在指向一个超过最后一个元素,即j == end()。您现在继续调用delete *j;,取消引用将触发断言的end()迭代器。

答案 2 :(得分:0)

更仔细地查看代码,看起来您的问题是由于元素的擦除

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

现在你正确使用了vector.erase()中新返回的迭代器,但问题是i和j都是同一向量的迭代器。这意味着发生以下情况

  • 'i'从形状矢量中删除
  • 形状向量在没有'i'元素的情况下重新分配
  • 'i'被重新分配给向量的内存块中的下一个元素(在i向后移动一个位置之后有剩余的元素)
  • 'j'仍然指向旧的记忆位置(不考虑洗牌)
  • 'j'从形状矢量(可能现在在最后一个元素之后)
  • 中删除
  • 越界错误

不要删除循环中的元素,最好在循环体内将元素标记为“死”,然后在std::remove_if的末尾使用while

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

其中isDeadfunctor定义的类似:

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