如何从向量中删除shared_ptr

时间:2012-11-19 22:35:13

标签: c++ vector shared-ptr

与下面的代码一样,m_vSprites是shred_ptr的向量,如果他的一个元素更新失败,我想从向量中删除它,但是当我想使用erase时我的代码崩溃了。但我不知道为什么,任何人都可以提供帮助?

我需要使用erase的原因是因为我的应用程序会不断地在向量中添加元素,但如果某些元素满足其杀死条件,它还会不断地从向量中擦除对象。如果我没有删除它,那么当程序运行时,向量会变得很大!

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

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

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


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

4 个答案:

答案 0 :(得分:5)

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

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

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

答案 1 :(得分: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);
    }
}

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

答案 2 :(得分:3)

我尝试使用erase-remove idiom

您可能希望从sprite类公开bool属性,并检查是否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

答案 3 :(得分: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类的析构函数内。