从列表中删除 STL

STL deleting from list

本文关键字:STL 删除 列表      更新时间:2023-10-16

我在使用 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'(又名'学生')和'字符串'(又名'basic_string,分配器>))的操作数无效" 我不太确定如何解决它。 谢谢,任何建议不胜感激!

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

您正在尝试将学生与字符串进行比较。默认情况下不会定义这样的比较,因此您必须自己定义一个正确的运算符,或者编写类似(*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;
}
}
  1. 您不推进迭代器。如果你碰巧擦除了第一个元素,你会崩溃,否则你会得到一个无限循环。然而。。。
  2. 有大量的例子如何正确迭代列表和擦除元素,例如:迭代 std::list 时擦除

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

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

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

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

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

你的循环实际上等效于以下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将不会更新,并且在循环的下一次迭代中,您将取消对不再存在的节点的迭代器的引用。这个问题的解决方案是知道erase函数返回什么,即下一个节点的迭代器。这意味着一个可行的解决方案看起来像

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