【发布时间】:2019-03-10 12:07:06
【问题描述】:
当我对它进行迭代时,我可以从 std::list 中删除元素吗?比如这样:
std::list<int> lst;
//....
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++)
{
if (*itr > 10)
lst.remove(*itr);
}
? 为什么?
【问题讨论】:
当我对它进行迭代时,我可以从 std::list 中删除元素吗?比如这样:
std::list<int> lst;
//....
for (std::list<int> itr = lst.begin(); itr != lst.end(); itr++)
{
if (*itr > 10)
lst.remove(*itr);
}
? 为什么?
【问题讨论】:
正确的代码如下:
for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); /*nothing*/)
{
if (*itr > 10)
itr = lst.erase(itr);
else
++itr;
}
当您从列表中删除一个项目时,您可能会使迭代器无效(如果它指向被删除的项目)。因此您需要使用erase 删除(它返回一个指向下一个项目的有效迭代器)。
更好的主意是使用std::remove_if:
bool greater_than_10(int x)
{
return x > 10;
}
lst.remove_if(greater_than_10);
如果你的编译器支持lambdas,你可以把它更短:
lst.remove_if([](int x){ return x > 10; });
(我没有测试这段代码,因为我的编译器不是那么新;幸运的是,lambda 函数是从@John Dibling 的答案中偷来的。)
实际上,从列表中删除会使 only the iterators pointing to the item being deleted 无效。但请注意,其他 STL 容器没有此属性。
因此,简而言之:一般来说,您不应该在迭代列表时从列表中删除项目,因为删除可能会使迭代器无效(并且程序可能会崩溃)。但是,如果您完全确定您删除的项目不是您在删除时使用的任何迭代器引用的值,您可以删除。
请注意,对于其他 STL 容器(例如向量),约束更加严格:从容器中删除不仅会使指向已删除项的迭代器失效,而且可能还会使其他迭代器失效!因此,在迭代容器时从这些容器中删除会更成问题。
【讨论】:
remove_if添加了示例代码,希望你不要介意。另外,我将itr 的类型从std::list 固定为std::list<int>::iterator。
没有。示例代码使itr 无效,导致未定义的行为。但这会起作用:
for (std::list<int>::iterator itr = lst.begin(); itr != lst.end(); )
{
if (*itr > 10)
itr = lst.erase(itr);
else
++itr;
}
【讨论】:
不,你不能。
但是你可以(并且应该)使用std::remove_if 和一个“大于10”的函子,像这样:
#include <list>
#include <algorithm>
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), std::bind2nd(std::greater<int>(), 10)), lst.end());
}
另一种更通用的方法是编写您自己的自定义函子。这是一个函子 is_a_match,如果要检查的值大于 10,则返回 true。您可以重新定义 operator() 以返回 true 以对应于在您的情况下“匹配”的任何含义:
#include <list>
#include <algorithm>
#include <functional>
struct is_a_match : public std::unary_function<int, bool>
{
is_a_match(int val) : val_(val) {};
bool operator()(int victim) const { return victim > val_; }
private:
int val_;
};
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), is_a_match(10) ));
}
如果您受益于符合 C++0x 的编译器,您还可以使用 lambda,这使得在许多情况下可以摆脱仿函数并编写更具表现力的代码
#include <list>
#include <algorithm>
int main()
{
std::list<int> lst;
lst.push_back(1);
lst.push_back(12);
lst.push_back(1);
//....
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int v) {return v > 10;}));
}
【讨论】:
list有自己的remove_if成员函数:lst.remove_if(std::bind2nd(std::greater<int>(), 10));
list 以外的集合执行此操作。我也更喜欢非成员函数,而不是 Scott Meyers 和扩展的一般思想。
我想你可以,但你必须在移除元素后重新分配迭代器,这可以使用erasemethod 而不是remove 来完成。
否则会不安全,不应该这样做。
【讨论】:
有关迭代器的说明,请参阅 http://www.cppreference.com/wiki/iterator/start。
几点说明:
++itr) 而不是后自增运算符 (itr++)【讨论】: