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==
重载函数。您可以定义此运算符或将studentName
与Student
中存储学生姓名的成员字符串变量进行比较。您可以考虑查看算法库中的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 从列表中删除的主要内容,如果未能解决你的问题,请参考以下文章