4

像下面的代码一样,m_vSprites 是一个 shred_ptr 的向量,如果他的一个元素更新失败,我想从向量中删除它,但是当我想使用擦除时我的代码崩溃了。但我不知道为什么,有人可以帮忙吗?

我需要使用擦除的原因是因为我的应用程序会不断地在向量中添加元素,但如果某些元素满足它们的终止条件,也会不断地从向量中擦除对象。如果我不删除它,随着程序的运行,矢量会变得很大!

RECT rcOldSpritePos;
    typedef boost::shared_ptr<Sprite> SmartSprite;
vector<SmartSprite>::iterator siSprite;
for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end();)
{
    // Save the old sprite position in case we need to restore it
    rcOldSpritePos = (*siSprite)->getPosition();

    if (!((*siSprite)->update()))
    {
        // Kill the sprite
        //siPreviousSprite = siSprite-1;
        siSprite = m_vSprites.erase(siSprite);
    }
            else
            {
                  siSprite++;
                  // See if the sprite collided with any others
          if (checkSpriteCollision(*siSprite))
        // Restore the old sprite position
        (*siSprite)->setPosition(rcOldSpritePos.left, rcOldSpritePos.top);
            }

}

我已经按照某人的建议更改了代码,但擦除功能仍然失败,请任何人有一些建议?

有关如何在 vecotr 中添加元素的更多信息

这里有什么问题吗?

    SmartSprite sprite;
if (0 < enemies.size())
{
    // Pick a random enemy to drop bomb
    size_t nRandEnemy = (rand() % enemies.size());
    RECT rRandEnemy = enemies.at(nRandEnemy)->getPosition();
    sprite.reset(new BombSprite(m_system, rRandEnemy.right-OBJECTSIZE/2,   
            rRandEnemy.bottom));
    m_vSprites.push_back(sprite);
}

我的精灵类没有任何析构函数......

另一个信息是,当我调试时,发现它在擦除内部函数中崩溃:_Destroy(_Mylast - 1, _Mylast);


问题解决了!,原因是在我的 Sprite 类中,我将它包装为一个智能指针,并创建了另一个智能指针作为其成员变量。现在我没有使用智能指针成员变量,系统也没有再次崩溃......我会继续思考为什么我不能在那个类中使用智能指针,当从向量中擦除精灵时,确实他的成员变量有关系吗?如果只使用原始指针而不是使用智能指针,我还需要删除该成员变量吗?

4

4 回答 4

6

如果您要擦除的迭代器是向量的开始迭代器,那么这个;

siPreviousSprite = siSprite - 1;

是未定义的行为。像这样重组你的循环:

for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); )
{

    rcOldSpritePos = (*siSprite)->getPosition();

    if (!((*siSprite)->update()))
    {
        // Kill the sprite
        siSprite = m_vSprites.erase(siSprite);            
    }
    else
    {
        ++siSprite;
    }
}

如果仍然崩溃,那么您的精灵类一定有问题。

于 2012-11-19T22:41:25.933 回答
3

我会尝试使用erase-remove idiom

您可能希望bool从 sprite 类中公开一个属性,并检查它是否true要从向量中删除 sprite。像这样(用 VC10/VS2010 SP1 测试的代码):

#include <algorithm>
#include <iostream>
#include <memory>
#include <ostream>
#include <vector>

using namespace std;


struct Sprite
{
    explicit Sprite(int spriteId)
     : kill(false)
     , id(spriteId)
    {
    }

    int id;
    bool kill;
};


int main()
{
    typedef shared_ptr<Sprite> SmartSprite;  
    typedef vector<SmartSprite> SpriteArray;

    SpriteArray sprites;
    for (int i = 0; i < 5; i++)
        sprites.push_back( make_shared<Sprite>(i*11) );

    for (auto it = sprites.begin(); it != sprites.end(); ++it)
        cout << (*it)->id << " ";
    cout << endl;

    sprites[2]->kill = true;
    sprites[3]->kill = true;

    // Remove sprites with kill flag set    
    sprites.erase
    (
        remove_if
        (
            sprites.begin(),
            sprites.end(),
            [](const SmartSprite& ps){ return ps->kill; }
        ),
        sprites.end()
    );

    for (auto it = sprites.begin(); it != sprites.end(); ++it)
        cout << (*it)->id << " ";
    cout << endl;
}

输出:

0 11 22 33 44
0 11 44
于 2012-11-19T23:50:37.773 回答
3

如果siSprite == m_vSprites.begin()siSprite-1是不合法的。编写这种循环的通常方法是

for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); )
{
    rcOldSpritePos = (*siSprite)->getPosition();
    if ((*siSprite)->update())
    {
         ++siSprite;
    }
    else
    {
        siSprite = m_vSprites.erase(siSprite);
    }
}

不确定这是否真的可以解决您的问题,因为您的错误可能是完全不相关的。

于 2012-11-19T22:40:53.270 回答
1

擦除可能会导致新的分配,迭代器可能会在那里中断。最好这样做:

for(size_t i=0; i<v.size(); ++i)
  if(!v[i].update())
  {
    v.erase(v.begin()+i);
    --i; // since the elements shifted up.
  }

否则你可能想使用队列,擦除操作在向量上是非常昂贵的。

编辑:

我已经用一个虚拟的 Sprite 类对其进行了测试并且工作正常,错误必须在 Sprite 类的析构函数内。

于 2012-11-19T23:00:30.443 回答