C++ 断断续续;“向量迭代器不可解引用”;

C++ 断断续续;“向量迭代器不可解引用”;,c++,vector,stl,iterator,C++,Vector,Stl,Iterator,我有以下代码,但当我运行它时,我会间歇性地得到一个调试断言失败错误,原因是“向量迭代器不可解引用” 我想用这个程序做的是创建一堆二维形状(正方形和圆形),然后一个接一个地移动它们,在移动过程中检查碰撞。当两个形状发生碰撞时,我希望删除它们,直到(最多)剩下一个形状。大约三分之一的尝试结果是成功的,程序发现四次碰撞,然后停止。其他时候,它会发现1-3次碰撞,然后出错 我也应该提到我对C++是比较新的(是的,这是一个课程作业),所以如果我的代码中有其他新手错误,请指出它们。p> 我的移动和碰撞方法本

我有以下代码,但当我运行它时,我会间歇性地得到一个调试断言失败错误,原因是“向量迭代器不可解引用”

我想用这个程序做的是创建一堆二维形状(正方形和圆形),然后一个接一个地移动它们,在移动过程中检查碰撞。当两个形状发生碰撞时,我希望删除它们,直到(最多)剩下一个形状。大约三分之一的尝试结果是成功的,程序发现四次碰撞,然后停止。其他时候,它会发现1-3次碰撞,然后出错

<>我也应该提到我对C++是比较新的(是的,这是一个课程作业),所以如果我的代码中有其他新手错误,请指出它们。p> 我的移动和碰撞方法本身似乎很好,所以这里没有包括它们

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)
        }
    }
}
Square*s1=新的Square(seedCoord(限制),seedCoord(限制),2.0);
... //这里创建了9个新形状(正方形和圆形)
向量形状;
形状。推回(s1);
... //所有9个形状都添加到向量中
int m=shape.size();
布尔碰撞=假;
while(m>1)//继续运行,直到只有一个形状没有碰撞为止
{       
for(auto i=shape.begin();i!=(shape.end());)//按顺序对每个形状执行移动
{   
collision=false;//将冲突布尔值设置为false
(*i)->移动(seedCoord(0.5),方向[(rand()%4)],限制);//对形状执行移动
for(auto j=shape.begin();j!=(shape.end()-1);)//按顺序检查与每个形状的冲突
{
if((*i)!=(*j))//检查碰撞时忽略self
{
如果((*i)->collide(*j))//检查碰撞
{
标准::cout引用:

迭代器和对已擦除元素以及它们与容器结尾之间的元素的引用将无效。结束迭代器之前的元素也将无效


我猜想,当您第二次调用
erase(j)
时,问题就出现了,
j
是一个迭代器,它指向
i
shape.end()之间的位置
并失效。

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

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


最后两个元素发生冲突,
i
end()-2
j
end()-1
。首先你
擦除(i)
,意味着所有元素都被推到左边一个,包括指向的
j
j
现在在概念上是无效的,但它只不过是一个指针,现在它指向最后一个元素的后面一个,即
j==end()
。您现在继续调用
delete*j;
,取消对
end()的引用
iterator wich将触发断言。

仔细查看代码,您的问题似乎是由于删除了元素

i = shape.erase(i); //Erase shape object
...
j = shape.erase(j); //Erase shape object
现在您正确地使用了vector.erase()中新返回的迭代器,但问题是i和j都是同一个向量的迭代器

  • “i”将从形状向量中删除

  • 形状向量在没有“i”元素的情况下重新分配
  • “i”被重新分配到向量内存块中的下一个元素(该内存块在
    i
    之后的剩余元素被向下移动了一个位置)
  • “j”仍然指向旧的内存位置(不考虑洗牌)
  • “j”从形状向量中删除(现在可能位于最后一个元素之后)
  • 越界误差
与其删除循环中的元素,不如在循环体中将元素标记为“死”,然后在
while
循环的末尾使用
std::remove\u if

shape.erase(std::remove_if(shape.begin(), 
                           shape.end(),
                           isDead),
                           shape.end());
其中,
isDead
是一个定义如下:

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

这通常意味着您的迭代器已经超出了边界(即,您已经偏离了向量的开头或结尾),因为您特别要求旁注:
i++
不在
if
中,而是接近底部,这有什么具体原因吗?您总是增加它,而正版本
if(collision){break;}
对我来说会更具可读性。@benjymous我认为这就是问题所在,我只是不明白为什么它只会偶尔发生(我尝试使用shape.end()-1来对抗它,但似乎没有多大帮助。@Hulk我想没有什么特别的原因,只是它使我的循环类似,以便我可以发现错误(以前从未使用过auto)。不过现在更改了,谢谢。谢谢!我添加了一个if语句来检查j>I,如果是,我已经切换了删除/擦除调用的顺序。这有点混乱,但已经解决了我的问题。这看起来比我已经有的解决方案更优雅-我以前从未使用过structs,所以我将学习这些,并尝试实现它是-谢谢!是的-如果你以前从未见过它,它看起来会有点奇怪!它叫函子-查看这篇文章了解更多关于它们用途的信息:“形状向量在没有'i'元素的情况下被重新分配”-不。删除元素时不会重新分配。但是,迭代器无效。@ArneMertz你说得对-只需重新分配,如果向量变大,我将编辑该位。