STL 从列表中删除

Posted

技术标签:

【中文标题】STL 从列表中删除【英文标题】:STL deleting from list 【发布时间】: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. 编译器的实际错误。 【参考方案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】:
    您不推进迭代器。如果你碰巧擦除了第一个元素,你会崩溃,否则你会死循环。但是,... 有大量示例如何正确地迭代列表和擦除元素,例如在这里: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 我知道它有问题,但很累才弄清楚。谢谢你提醒我。

以上是关于STL 从列表中删除的主要内容,如果未能解决你的问题,请参考以下文章

创建和管理STL列表的子列表

使用 STL 从字符串中删除重复字符

在迭代时从地图(或任何其他STL容器)中删除/删除内容

使用 STL 从文件中删除除最后 500,000 个字节之外的所有字节

怎么删除STL容器的元素

C++ STL删除vector中指定元素