STL从列表中删除

时间:2017-10-21 21:01:28

标签: c++ stl iterator

我在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);
                    }
                }

我收到错误&#34;二进制表达式的操作数无效(&#39; value_type&#39;(又名&#39;学生&#39;)和&#39;字符串&#39;(又名&#39; ; basic_string,allocator&gt;&#39;))&#34; 我不太确定如何解决它。 谢谢,任何建议表示赞赏!

6 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分: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;
}

此外,如果学生的姓名是私人或受保护的,并且无法从公共语境访问它,那么您还必须在学生定义中添加朋友声明:

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;
    }
}

答案 2 :(得分:1)

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

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

修改:现在您已发布错误,很明显您还有其他问题。看看每个人如何解决这个问题的答案。

答案 3 :(得分:1)

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

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

注意for循环的最后一部分是如何为空的?这意味着你永远不会增加或修改it语句中的变量for也不会修改循环体!这意味着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将不会被更新,并且循环的下一次迭代将取消引用一个不再存在的节点的迭代器。 问题的解决方案是知道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
    }
}

答案 4 :(得分:1)

  1. 你没有推进迭代器。如果您碰巧擦除了第一个元素,您将崩溃,否则您将获得无限循环。但是,......
  2. 有很多例子可以正确地迭代列表和擦除元素,例如这里:Erasing while iterating an std::list

答案 5 :(得分:1)

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

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