简体   繁体   English

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

[英]C++ Vector.erase() last element corrupts iterator

I currently have a problem with vector.erase(). 我目前遇到vector.erase()的问题。

vector<gameObject> gameObjects;

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

So gameObject is the base class for everything inside the game and it has a bool flag that basically tells us if the object was destroyed. 所以gameObject是游戏中所有内容的基类,它有一个bool标志,基本上告诉我们对象是否被破坏。 If the flag is set it should be removed from the vector. 如果设置了标志,则应从向量中删除该标志。

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

Now the first destroyed object gets removed from the vector successfully and then I get get an error that iterator is not dereferencable, pointing to the vector library at line 73(?). 现在第一个被破坏的对象被成功地从向量中移除然后我得到一个错误:迭代器不是可解除引用的,指向第73行(?)处的向量库。

I then check with the msvc debugger. 然后我检查msvc调试器。 In the data preview it shows that iterator points to the last/newest element of gameObjects. 在数据预览中,它显示迭代器指向gameObjects的最后/最新元素。 It is then removed (erase(it)) and AFTERWARDS the data preview doesn't change and calling it->getDestroyed() results in the error message. 然后将其删除(擦除(it))并且AFTERWARDS数据预览不会更改,并且调用它 - > getDestroyed()会导致错误消息。

Debug assertion failed! 调试断言失败了! vector iterator not dereferencible. 矢量迭代器不可解除引用。

PS: I checked cplusplus.com and vector.erase should return a new, valid iterator so I'm not sure where I'm messing it up. PS:我检查了cplusplus.com,vector.erase应该返回一个新的,有效的迭代器,所以我不知道我在哪里弄乱它。

€: After I was told about the erase-remove idiom I went ahead and ended up with the following, which doesn't compile. €:在我被告知擦除 - 移除成语后,我继续前进,最后得到以下内容,但不能编译。 Due to my function being a member of gameObject I'm not sure how to successfully call remove_if. 由于我的函数是gameObject的成员,我不知道如何成功调用remove_if。 Thanks 谢谢

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

€2: A lot of you pointed out the first object isn't being checked. €2:很多人指出第一个对象没有被检查。 I propably should've pointed that out but the first element is ALWAYS the player and shouldn't be removed. 我应该指出这一点,但第一个元素始终是玩家,不应该删除。 Thanks for your comments nevertheless. 不过,谢谢你的评论。 I'll try with a simple forward loop without getting too fancy ^^. 我会尝试一个简单的前进循环而不会太花哨^^。

€3: I tried Jonathan Mees suggested code but I get the exact same error message. €3:我尝试了Jonathan Mees建议的代码,但我得到了完全相同的错误消息。 I'll try and find out where exactly it happens but I can't just put a breakpoint into the erasing part anymore. 我会试着找出它到底发生了什么但是我不能再把断点放到擦除部分了。 Will mess around a bit. 会有点乱。

€4: Problem was solved by removing the else {} condition and always decrementing the iterator. €4:通过删除else {}条件并始终减少迭代器来解决问题。 Thanks again for all your replies. 再次感谢您的回复。

Let's say you have 2 objects in your vector and the last one is is marked as destroyed. 假设你的向量中有2个对象,最后一个被标记为已销毁。 When you call erase , it will return a new, valid iterator pointing at the element after the erased element. 当你调用erase ,它将返回一个新的有效迭代器,指向擦除元素后面的元素。 There is no element after the erased element, so the returned iterator is gameObjects.end() . 擦除元素后面没有元素,因此返回的迭代器是gameObjects.end() You then continue to the top of the loop and dereference this iterator, which is not valid. 然后继续到循环的顶部并取消引用此迭代器,这是无效的。 You need to decrement your iterator after the erase if you want it pointing at a valid element. 如果希望指向有效元素,则需要在擦除后递减迭代器。

One other note: If you ever wanted your first element removed, it will not be. 另一个注意事项:如果您想要删除第一个元素,则不会。 Your loop exits when the iterator == gameObjects.begin() , so the first element is never checked. 当迭代器== gameObjects.begin() ,你的循环退出,所以永远不会检查第一个元素。

Is there some reason you wanted to do this in reverse? 有什么理由你想反过来这样做吗? If there is no specific reason, I would recommend you use the method recommended by @Borgleader. 如果没有具体原因,我建议您使用@Borgleader推荐的方法。

Your loop is a little messed up - you're iterating backwards, ignoring the first element, and testing some elements multiple times. 你的循环有点混乱 - 你向后迭代,忽略第一个元素,并多次测试一些元素。 Might I suggest rbegin() as an alternative? 我可以建议将rbegin()作为替代方案吗?

vector::erase returns the: vector::erase返回:

Iterator following the last removed element. Iterator跟随最后删除的元素。 If the iterator pos refers to the last element, the end() iterator is returned. 如果迭代器pos引用最后一个元素,则返回end()迭代器。

Meaning that vector::erase will never return vector::begin (unless you removed the only element in the container.) So it will always be dereferenced again after vector::erase is called. 这意味着vector::erase永远不会返回vector::begin (除非你删除了容器中唯一的元素。)所以在调用vector::erase之后it总会被解除引用。 It will be dereferenced even if vector::end was returned by the call to vector::erase which is of course illegal. 即使通过调用vector::erase返回vector::end也会被解除引用,这当然是非法的。

Instead of this loop, consider using remove_if which is designed for this purpose: 而不是这个循环,考虑使用为此目的设计的remove_if

gameObjects.erase(remove_if(begin(gameObjects),
                            end(gameObjects),
                            [](const auto& i){ return i.getDestroyed(); }), end(gameObjects));

EDIT: 编辑:

I noticed you try to use this in your edit. 我注意到你尝试在编辑中使用它。 You cannot use a bare function pointer as the predicate. 您不能使用裸函数指针作为谓词。 If you want to avoid a lambda, you should consider the use of mem_fn : 如果你想避免lambda,你应该考虑使用mem_fn

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

Live Example 实例

If there's difficulty in reading that line feel free to use as many variable as you like: 如果阅读该行很困难,可随意使用尽可能多的变量:

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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM