C ++ Vector.erase()最后一个元素破坏了迭代器

时间:2016-12-22 16:44:23

标签: c++ vector iterator

我目前遇到了vector.erase()的问题。

vector<gameObject> gameObjects;

for (auto it = gameObjects.end() - 1; it != gameObjects.begin();)
    {
        if ((it)->getDestroyed()) {
            it = gameObjects.erase(it);
        }
        else {
            --it;
        }
    }

所以gameObject是游戏中所有内容的基类,它有一个bool标志,基本上告诉我们对象是否被破坏。如果设置了标志,则应从向量中删除该标志。

class gameObject
{
protected:
    bool toBeDestroyed;
public:
    bool getDestroyed();
    void markToDestroy();
};

现在第一个被破坏的对象被成功地从向量中移除然后我得到一个错误:迭代器不是可解除引用的,指向第73行的矢量库(?)。

然后我用msvc调试器检查。在数据预览中,它显示迭代器指向gameObjects的最后/最新元素。然后将其删除(擦除(it)),然后在数据预览未更改并调用它之后 - > getDestroyed()会导致错误消息。

调试断言失败了!矢量迭代器不能解除引用。

PS:我检查了cplusplus.com,vector.erase应该返回一个新的,有效的迭代器,所以我不确定我在哪里弄乱它。

€:在我被告知擦除 - 移除成语后,我继续前进,最后得到以下内容,但是没有编译。由于我的函数是gameObject的成员,我不知道如何成功调用remove_if。感谢

gameObjects.erase(remove_if(gameObjects.begin(), gameObjects.end(), gameObject::getDestroyed), gameObjects.end());

€2:很多人指出第一个对象没有被检查。我应该指出这一点,但第一个元素始终是玩家,不应该被删除。不过,谢谢你的评论。我会尝试一个简单的前向循环而不会太过花哨^^。

€3:我尝试过Jonathan Mees建议的代码,但我得到了完全相同的错误消息。我会尝试找出确切的位置,但我不能再将断点放入擦除部分了。会乱七八糟。

€4:通过删除else {}条件并始终递减迭代器来解决问题。再次感谢您的回复。

3 个答案:

答案 0 :(得分:5)

假设你的向量中有2个对象,最后一个被标记为已销毁。当你调用erase时,它将返回一个新的有效迭代器,指向擦除元素后面的元素。擦除元素后面没有元素,因此返回的迭代器为gameObjects.end()。然后继续到循环的顶部并取消引用此迭代器,这是无效的。如果希望它指向有效元素,则需要在擦除后递减迭代器。

另一个注意事项:如果您想要删除第一个元素,则不会。当迭代器== gameObjects.begin()时,循环退出,因此永远不会检查第一个元素。

你有什么理由反向做这件事吗?如果没有具体原因,我建议您使用@Borgleader推荐的方法。

答案 1 :(得分:3)

你的循环有点乱 - 你向后迭代,忽略第一个元素,并多次测试一些元素。我可以建议将rbegin()作为替代方案吗?

答案 2 :(得分:3)

vector::erase返回:

  

跟随最后删除的元素的迭代器。如果迭代器pos引用最后一个元素,则返回vector::erase迭代器。

意味着vector::begin将永远不会返回it(除非您删除了容器中唯一的元素。)因此,vector::erase将在调用vector::end后再次取消引用。如果vector::erase的调用返回gameObjects.erase(remove_if(begin(gameObjects), end(gameObjects), [](const auto& i){ return i.getDestroyed(); }), end(gameObjects)); ,那么它将被取消引用甚至,这当然是非法的。

考虑使用专为此目的设计的remove_if而不是此循环:

gameObjects.erase(remove_if(begin(gameObjects),
                            end(gameObjects),
                            mem_fn(&gameObject::getDestroyed)), end(gameObjects));

修改

我注意到您尝试在编辑中使用此功能。您不能使用裸函数指针作为谓词。如果你想避免使用lambda,你应该考虑使用mem_fn

auto p = mem_fn(&gameObject::getDestroyed);
auto result = remove_if(begin(gameObjects), end(gameObjects), p);

gameObjects.erase(result, end(gameObjects));

Live Example

如果阅读该行很困难,可随意使用尽可能多的变量:

{{1}}