关于std :: vector :: end()的问题

Max*_*Max 2 c++ stl stdvector

我最近在以下函数中修复了一个错误,答案让我感到惊讶.我有以下功能(在我发现错误之前编写):

    void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
    {
        vector<itemPtr>::iterator it; // itemPtr is a typedef for a std::tr1::shared_ptr<item::Item>
        for(it=items.begin(); it!=items.end(); ++it)
        {
            if((*it)->getPosition() == pt)
            {
                item::Item item(**it);
                items.erase(it);
                vect.push_back(item);
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

此函数查找Item"项目"向量中具有特定位置的所有对象,将其从"项目"中删除,并将它们放在"vect"中.之后,名为的函数putItemsAt执行相反的操作,并将项添加到"项".第一次通过,getItemsAt工作正常.putItemsAt但是,在调用之后,for循环getItemsAt将在'items'结束时运行.'它'将指向无效Item指针和段错误getPosition().在预感中,我改变it!=items.end()it<items.end(),并且它起作用了.谁能告诉我为什么?环顾SO表明它可能涉及erase使迭代器无效,但它仍然没有意义,为什么它会第一次工作.

我也很好奇,因为我计划将"项目"从向量更改为列表,因为列表的擦除效率更高.我知道我必须使用!=列表,因为它没有<运算符.我会使用列表遇到同样的问题吗?

Rem*_*eau 10

当您调用erase()时,该迭代器将变为无效.因为那是你的循环迭代器,在使它失效后调用它上面的'++'运算符是未定义的行为.erase()返回一个新的有效迭代器,指向向量中的下一个项目.你需要在循环中使用那个新的迭代器,即:

void Level::getItemsAt(vector<item::Item>& vect, const Point& pt) 
{ 
    vector<itemPtr>::iterator it = items.begin();
    while( it != items.end() )
    {
        if( (*it)->getPosition() == pt )
        {
            item::Item item(**it);
            it = items.erase(it);
            vect.push_back(item);
        }
        else
            ++it;
    } 
} 
Run Code Online (Sandbox Code Playgroud)


Bil*_*eal 5

您正在调用未定义的行为.向量的所有迭代器都被调用erase该向量的事实无效.对于实现来说它做任何想做的事都是完全有效的.

当你打电话时items.erase(it);,it现在无效.为了符合标准,你现在必须假设it已经死了.

您可以在下次调用时使用该无效迭代器调用未定义的行为vect.push_back.

您可以使用循环it的跟踪变量再次调用未定义的行为for.

您可以使用以使代码有效std::remove_copy_if.

class ItemIsAtPoint : std::unary_function<bool, item::Item>
{
    Point pt;
public:
    ItemIsAtPoint(const Point& inPt) : pt(inPt) {}
    bool operator()(const item::Item* input)
    {
        return input->GetPosition() == pt;
    }
};

void Level::getItemsAt(vector<item::Item>& vect, const Point& pt)
{
    std::size_t oldSize = items.size();
    std::remove_copy_if(items.begin(), items.end(), std::back_inserter(vect), 
        ItemIsAtPoint(pt));
    items.resize(vect.size() - (items.size() - oldSize));
}
Run Code Online (Sandbox Code Playgroud)

如果你正在使用它boost::bind,你可以使它更漂亮,但这是有效的.