【问题标题】:STL deleting from listSTL 从列表中删除
【发布时间】:2017-10-21 21:01:28
【问题描述】:

我在使用 STl 时遇到了问题。我试图遍历学生对象的 STL 列表。当我找到匹配的比较时,我试图删除对象。但是,我在进行比较时收到错误消息。这是我到目前为止所做的:

 string studentName;
                cout<<"Enter name of student to remove";
                cin>>studentName;

                list<Student>::iterator it = studentList.begin();
                while (it != studentList.end()){
                    if(*it== studentName){
                        studentList.erase(it);
                    }
                }

我收到错误“二进制表达式的无效操作数('value_type'(又名'Student')和'string'(又名'basic_string,分配器>'))” 我不太确定如何解决它。 谢谢,任何建议都值得赞赏!

【问题讨论】:

  • 您忘记的事情: 1. 一个完整的代码示例。 (例如,studentList 从未定义) 2. 编译器的实际错误。

标签: c++ stl iterator


【解决方案1】:

您正在将Student 的实例与std::string 进行比较,我假设它没有定义的operator== 重载函数。您可以定义此运算符或将studentNameStudent 中存储学生姓名的成员字符串变量进行比较。您可以考虑查看算法库中的std::remove_if,您可以使用它来过滤掉任何没有该名称的学生。

【讨论】:

    【解决方案2】:

    您正在尝试将 Student 与字符串进行比较。默认情况下未定义此类比较,因此您必须自己定义适当的运算符或编写类似(*it).getName() == studentName 的内容,其中 getName 是 Student 的成员函数,它返回学生的姓名。 此外,您的 for 循环不正确。应该是这样的:

    for(auto it = studentList.begin(); it != studentList.end();) {
        if((*it).getName() == studentName) {
            it = studentList.erase(it);
        } else {
            ++it;
        }
    }
    

    编辑:如果您决定重载比较运算符,那么这里有一个提示:

    bool operator==(const Student& student, const std::string& name) {
        return student.getName() == name;
    }
    
    bool operator==(const std::string& name, const Student& student) {
        return student == name;
    }
    
    bool operator!=(const Student& student, const std::string& name) {
        return !(student == name);
    }
    
    bool operator!=(const std::string& name, const Student& student) {
        return !(student == name);
    }
    

    对于这个问题,上述四个重载中的第一个就足够了,但通常最好定义几个版本以避免将来出现任何意外。此外,如果 Student 类没有像 getName 这样的成员函数(强烈建议使用这样的函数,除非 Student 是一个所有数据成员都公开的简单结构。)那么你必须更改第一个重载(其余的参考到第一个,所以他们会自动适应变化。)像这样:

    bool operator==(const Student& student, const std::string& name) {
        return student.name == name;
    }
    

    此外,如果 Student 的名称是私有的或受保护的,并且无法从公共上下文中访问它,那么您还必须在 Student 定义中添加一个朋友声明:

    class Student {
    public:
    
    // Public interface...
    
    private:
        std::string name;
    
        friend bool operator==(const Student& student, const std::string& name);
    };
    

    友元声明的位置无关紧要,只要它在类的定义中。同样,您只需要将第一个重载设为特权,因为其余重载只调用第一个。
    现在可以更改循环:

    for(auto it = studentList.begin(); it != studentList.end();) {
        if(*it == studentName) {
            it = studentList.erase(it);
        } else {
            ++it;
        }
    }
    

    【讨论】:

    • 您的解决方案完美运行!出于学习目的,您能否给我一些建议,告诉我如何重载 == 运算符,因为我正在使用迭代器
    • 是否使用迭代器并不重要。我已经编辑了答案,以向您展示如何重载运算符。
    【解决方案3】:
    1. 您不推进迭代器。如果你碰巧擦除了第一个元素,你会崩溃,否则你会死循环。但是,...
    2. 有大量示例如何正确地迭代列表和擦除元素,例如在这里:Erasing while iterating an std::list

    【讨论】:

    • 我修复了循环,但它在 if(*it== studentName) 处引发错误
    【解决方案4】:

    当您删除迭代器指向的列表段时,迭代器不再有效。这就是为什么erase 返回一个新的迭代器到被擦除的元素之后的元素。此外,您可能希望在循环中的某个点增加迭代器。试试这个:

    while (it != studentList.end()){
        if(*it == studentName)
            it = studentList.erase(it);
        else
            ++it;
    }
    

    编辑:现在您已经发布了错误,很明显您还有另一个问题。看看每个人的回答如何解决这个问题。

    【讨论】:

      【解决方案5】:

      您正在尝试比较字符串和学生。此外,您没有推进迭代器,因此该循环将无法停止。尝试以下方式:

      while (it != studentList.end()) {
          if(it->getName == studentName) {
              it = studentList.erase(it);
          }
          ++it;
      }
      

      【讨论】:

        【解决方案6】:

        您的循环实际上等同于以下for 循环:

        for (list<Student>::iterator it = studentList.begin();
             it != studentList.end();
             /* EMPTY */)
        {
            if(*it== studentName){
                studentList.erase(it);
            }
        }
        

        注意for 循环的最后一部分是空的吗?这意味着您永远不会在for 语句中增加或修改变量it循环体也不会!这意味着it 永远不会改变并且您有一个无限循环。

        解决这个问题的简单而明显的方法是在循环中增加it。回到原来的循环,添加修复:

        list<Student>::iterator it = studentList.begin();
        while (it != studentList.end()){
            if(*it== studentName){
                studentList.erase(it);
            }
        
            ++it;  // Make iterator "point" to the next node
        }
        

        然而此修复在另一个方面存在缺陷。这是因为当你删除一个节点时,你会跳过你删除的节点之后的那个节点,所以你错过了一个节点。幼稚的解决方案是仅在不删除节点时增加 it

        while (it != studentList.end()){
            if(*it== studentName){
                studentList.erase(it);
            } else {
                ++it;  // Make iterator "point" to the next node
            }
        }
        

        此解决方案存在缺陷,如果您删除一个节点,您将有未定义的行为。这是因为 it 将不会被更新,并且循环的下一次迭代您将取消对不再存在的节点的迭代器的引用。 this 问题的解决方案是知道the erase function returns 是什么,即指向下一个节点的迭代器。这意味着一个可行的解决方案看起来像

        while (it != studentList.end()){
            if(*it== studentName){
                it = studentList.erase(it);  // Make iterator "point" to node after removed node
            } else {
                ++it;  // Make iterator "point" to the next node
            }
        }
        

        【讨论】:

        • 最后一个 for 循环是错误的。如果循环找到一个等于studentName 的对象,那么它将跳过下一个元素——它根本不会检查它。所以我会坚持最后一个解决方案。
        • @navyblue 我知道它有问题,但很累才弄清楚。谢谢你提醒我。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-30
        • 1970-01-01
        • 1970-01-01
        • 2012-12-10
        • 1970-01-01
        相关资源
        最近更新 更多