访问迭代器指向的列表元素

时间:2011-02-28 18:33:49

标签: c++ visual-c++ list stl

自然的答案是取消引用迭代器并获取值。但是,我坚持使用VC ++ 2010,它不允许解除引用列表迭代器(或者是吗?) 我很困惑,因为在某一点上,我需要取消引用两个列表迭代器并使用以下方法比较它们的值: (* it)==(* it2) 程序因错误而崩溃,仅此行。我也在声明中取消引用迭代器: printf(“%d \ n”,(* it)); 这虽然工作得很好。 那么,有没有办法在不解除引用或使用cliext :: list的情况下访问元素。

for (it=sList.begin(); it != sList.end(); it++)
{
    for (it2=it; it2 != sList.end(); it2++)
    {
        it2++;
        if ((*it) == (*it2))
        {
           sList.erase(it, it2);
        }
        it2--;
    }
}

我得到的错误是:

Debug Assertion Failed

Expression:list iterator not dereferencable

令人惊讶的是,在DevC ++(MinGW)

上编译时,相同的代码运行没有问题

5 个答案:

答案 0 :(得分:4)

您实际上可以取消引用list迭代器。如果你不能,你的比较代码甚至不会编译。很可能你不小心解除引用end迭代器而不是有效迭代器,导致崩溃。没有更多的代码,很难进一步观察。

编辑:我无法弄清楚你正在尝试做什么。您编写的代码将擦除两个相等元素之间的所有元素。我假设您实际上是在尝试删除所有重复的元素,并且首先对列表进行排序以获得性能不是一个问题/选项。

EDIT2:我在下面的评论中看到你真的要删除范围。更新了我的代码。

然后尝试这样的事情:

for (it=sList.begin(); it != sList.end(); ++it)
{
    it2 = it;
    ++it2;
    while(it2 != sList.end())
    {
        if ((*it) == (*it2))
        {
           it = it2 = sList.erase(it, it2);  // Reset it as well since it will be blown away. It'll still point to the same value it did before though.
        }
        else
            ++it2;
    }
}

答案 1 :(得分:1)

  

“选择”不会破碎。

     

很少在操作系统中找到错误或   编译器,甚至是第三方   产品或图书馆。这个bug最多   可能在申请中。 - 来自 The   务实的程序员

这很可能是由于你的问题,而不是MS。在使用它们时确保您的迭代器未失效。您可能会意外删除使迭代器无效的元素。检查此帖子:What is the lifetime and validity of C++ iterators? 还有祝你好运! :)

更新:

正如我前面提到的,你通过在循环中间擦除它们来使你的迭代器失效。请参阅下面的代码以正确完成。

std::list<int>::iterator EraseElements(std::list<int>& sList, std::list<int>::iterator start)
{
    for (std::list<int>::iterator itor1 = start; itor1 != sList.end(); ++itor1)
    {
        std::list<int>::iterator itor2(itor1);
        ++itor2;

        for ( ; itor2 != sList.end(); ++itor2)
        {
            if ((*itor1) == (*itor2))
            {
                return sList.erase(itor1, itor2);
            }
        }
    }

    return sList.end();
}


void main()
{
    // Test
    list<int> sList;
    sList.push_back(1);

    // elements will be erased
    sList.push_back(2);
    sList.push_back(3);
    //
    sList.push_back(2);

    sList.push_back(4);
    sList.push_back(5);

    // elements will be erased
    sList.push_back(6);
    sList.push_back(7);
    //
    sList.push_back(6);


    list<int>::iterator next = sList.begin();
    while (next != sList.end())
    {
        next = EraseElements(sList, next);
    }

    // It will print 1 2 4 5 6
    for (std::list<int>::const_iterator itor = sList.begin(); itor != sList.end(); ++itor)
    {
        cout << *itor << endl;
    }
}

答案 2 :(得分:1)

肯定是你的代码。就我所见,它有两个问题。查看评论。

for (it2=it; it2 != sList.end(); it2++)
    {
        it2++;
            // there is no guarantee that it2 will now be valid
            // it should be validated again
        if ((*it) == (*it2))
        {
            // you should not modify the list here.
            // this will invalidate your iterators by default.
           sList.erase(it, it2);
        }
        it2--;
    }

答案 3 :(得分:1)

请改为尝试:

for (it=sList.begin(); it != sList.end(); it++)
{
    for (it2=sList.end()-1; it2 != it+1; it2--)
    {
        if ((*it) == (*it2))
        {
            it = sList.erase(it, it2)-1;
            break;
        }
    }
}

此新版本避免了原始版本代码中的两个错误。首先,代码现在正确处理内部for循环的边缘条件。在原始代码中,for循环允许it2上升到sList.end()-1,但是下一行在最后一次迭代时将其递增到sList.end()。然后下一行取消引用这个(无效的)迭代器,它是一个超过列表最后一个值的迭代器(因为这是end返回的内容,它不是列表最后一个值的迭代器。)

其次,调用erase使任何指向已擦除的值的迭代器无效(在这种情况下,它将包括从itit2-1的任何迭代器)。通过从列表的末尾开始并继续前进,我们不再需要在找到值时继续迭代,并且一旦找到它就可以从内循环中breakerase在删除元素后返回列表中下一个元素的迭代器(这将是我们想要为it尝试的下一个元素)。但是由于for循环递增it,我们从erase返回的值中减去1,这样it一旦在下一个循环迭代开始时递增,就指向右边的元素。 (注意,在it指向第一个元素的情况下,我们实际上暂时将其设置为在列表开头之前指向一个元素;但是,这只是临时的,我们不会取消引用迭代器,而它是指向列表之外)。

请注意,这会保留案例0 2 3 4 5 1 6 7 8 0 9 10 11 1的代码的原始行为。你没有明确说明删除应该发生的顺序(首先要删除0之间的元素,还是1之间的元素,或者我们是否需要添加额外的逻辑擦除除第一个01之外的整个范围?),但此代码的行为与原始代码相同,并删除0之间的数字,并忽略9 10 11之间的数字{1}}之后在匹配1之间是原创的。

答案 4 :(得分:0)

目前还不清楚这段代码片段或您收到错误的代码是什么。

你想要做的就是每个项目删除它与下一个匹配项目之间的所有项目,或者它可能是最后一个匹配的项目。

你的内循环迭代是从循环增量双重步进,然后在循环内再次递增。

你没有检查你是否在进行内部迭代后命中/通过了列表的末尾,这可能会导致比较时发生崩溃

擦除后,你减去it2,然后将它放在it1之前(现在被删除)。