【问题标题】:C++ - begin() returns the end() iterator with a non-empty listC++ - begin() 返回带有非空列表的 end() 迭代器
【发布时间】:2020-07-12 14:39:13
【问题描述】:

正如问题所暗示的,我对迭代器和列表有一种非常奇怪的行为。 因此,(类)问题要求一个函数从列表中删除满足条件的所有元素,当我试图涵盖我有一个所有元素都相同的列表的情况时,我发现最后一个元素仍然存在.

代码如下:

void esborra_tots(list<Estudiant>& t, int x) {
    list<Estudiant>::iterator it;
    list<Estudiant>::iterator itend = t.end();

    for (it = t.begin(); it != t.end(); it++) {
        if ((*it).consultar_DNI() == x) {

            t.erase(it);
            if (t.empty()) return;
            else it = t.begin();
        }
    }
}

我使用 itend 只是为了在调试时查看值。 这是会议:

这怎么可能? PD:我不是在寻找解决问题的其他方法。

【问题讨论】:

    标签: c++ c++11 visual-studio-code


    【解决方案1】:

    这是因为您在 it = t.begin() 之后执行 it++(循环后操作)。

    删除它,并在你的循环体中粘贴一个else it++(虽然我更喜欢++it);它只需要在您进行擦除时发生。

    这类似于erasing from a map while iterating over it的方法。

    从该方法中得到提示,我们可以注意到您的 else it = t.begin() 是浪费的:您每次都从循环的开头开始,这增加了算法的算法复杂度。

    改为使用the iterator returned by erase

    void esborra_tots(list<Estudiant>& t, int x) {
        list<Estudiant>::iterator it;
        list<Estudiant>::iterator itend = t.end();
    
        for (it = t.begin(); it != t.end(); ) {
            if ((*it).consultar_DNI() == x) {
                it = t.erase(it);
            }
            else {
                ++it;
            }
        }
    }
    

    注意我们也不再需要检查列表是否为空;如果它现在是空的,it 将是 t.end() 并且循环无论如何都会结束。


    目前,您没有使用itend。出于显而易见的原因,您不能只是将其交换到循环条件中,但是如果您在它失效时将其重置,那么它可能是值得的:

    void esborra_tots(list<Estudiant>& t, int x) {
        for (auto it = t.begin(), end = t.end(); it != end; ) {
            if (it->consultar_DNI() == x) {
                it = t.erase(it);
                end = t.end();
            }
            else {
                ++it;
            }
        }
    }
    

    但是,坚持使用原始代码但删除未使用的 itend 声明可能更清楚。

    【讨论】:

    • 或者避免所有这些并使用erase-remove idiom,如t.remove_if([x](const auto&amp; elem) { return elem.consultar_DNI() == x; })。不过,+1
    • 这非常有用!!由于隔离而没有老师,这是一个真正的问题!
    • @NathanOliver 或者那样,虽然我觉得写出的循环更清晰。对于list,我也很想知道性能比较是什么样的
    • AFAIK, list::remove_if 应该进行优化,使其比一般方法更快。今晚晚些时候我可能会尝试写一些测试来看看。
    • @NathanOliver 哦,我错了,我错过了它是会员;正在想象免费的remove_if 加上erase。所以,真的,这根本不是擦除删除成语。
    【解决方案2】:

    函数中使用的方法

    void esborra_tots(list<Estudiant>& t, int x) {
        list<Estudiant>::iterator it;
        list<Estudiant>::iterator itend = t.end();
    
        for (it = t.begin(); it != t.end(); it++) {
            if ((*it).consultar_DNI() == x) {
    
                t.erase(it);
                if (t.empty()) return;
                else it = t.begin();
            }
        }
    }
    

    无效。调用erase方法后迭代器失效。

    你可以这样写函数

    void esborra_tots(list<Estudiant>& t, int x) {
        for ( auto it = t.begin(); it != t.end(); ) {
            if ((*it).consultar_DNI() == x) {
    
                it = t.erase(it);
            }
            else {
                ++it;
            } 
        }
    }
    

    但这太复杂了。如果使用 remove_if 方法,函数看起来会简单得多。

    例如

    void esborra_tots(list<Estudiant>& t, int x) {
        t.remove_if( [&x]( const auto &item ) { return item.consultar_DNI() == x; } );
    }
    

    这是一个演示程序。

    #include <iostream>
    #include <list>
    
    int main() 
    {
        std::list<int> list = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    
        for ( const auto &item : list ) std::cout << item << ' ';
        std::cout << '\n';
    
        list.remove_if( []( const auto &item ){ return item % 2 == 0; } );
    
        for ( const auto &item : list ) std::cout << item << ' ';
        std::cout << '\n';
    
        return 0;
    }
    

    它的输出是

    0 1 2 3 4 5 6 7 8 9 
    1 3 5 7 9 
    

    【讨论】:

    • 感谢您的回答,但正如我所说,我不是在寻找其他方法,大学规则。
    • @PaueteGalopa 我非常怀疑是否有这样的规则。您可以展示您对列表的了解。
    • 如果我使用课堂上没有教过的方法,问题/练习分数将为 0。这就是它的工作原理,我也不喜欢它!想想他们是在教我们如何用最少的资源解决问题。将来我将有足够的时间使用所有 STL 功能!我已经使用您的回答来熟悉 remove_if,它很有用且解释清楚,谢谢 Vlad!
    • @PaueteGalopa 学习不恰当的 C++ 内容只是为了获得分数更糟糕。你应该指出老师,他犯了一个很大的错误,没有告诉你无效的迭代器。如果您在面试中对此进行编程,您肯定不会被要求参加下一次会议。
    • @rioV8 我在上大学,是 4 到 5 年课程的第一年。在回答此类问题之前,请先考虑一下您不知道的完整教学大纲。 fib.upc.edu/en/studies/bachelors-degrees/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-14
    • 2020-09-19
    • 2016-11-26
    • 2014-07-30
    相关资源
    最近更新 更多