C++ 迭代向量,边走边删除某些项目
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1604588/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
iterate vector, remove certain items as I go
提问by cchampion
I have a std::vector m_vPaths; I will iterate this vector and call ::DeleteFile(strPath) as I go. If I successfully delete the file, I will remove it from the vector. My question is can I get around having to use two vectors? Is there different data structure that might be better suited for what I need to do?
我有一个 std::vector m_vPaths; 我将迭代这个向量并随时调用 ::DeleteFile(strPath) 。如果我成功删除了文件,我会将其从向量中删除。我的问题是我可以避免使用两个向量吗?是否有不同的数据结构可能更适合我需要做的事情?
example: using iterators almost does what I want, but problem is once you erase using an iterator, all iterators become invalid.
示例:使用迭代器几乎可以满足我的要求,但问题是一旦使用迭代器擦除,所有迭代器都将无效。
std::vector<std::string> iter = m_vPaths.begin();
for( ; iter != m_vPaths.end(); iter++) {
std::string strPath = *iter;
if(::DeleteFile(strPath.c_str())) {
m_vPaths.erase(iter);
//Now my interators are invalid because I used erase,
//but I want to continue deleteing the files remaining in my vector.
}
}
I can use two vectors and I will no longer have a problem, but is there a better, more efficient method of doing what I'm trying to do?
我可以使用两个向量,我将不再有问题,但是有没有更好、更有效的方法来做我想做的事情?
btw, incase it is unclear, m_vPaths is declared like this (in my class):
顺便说一句,如果不清楚,m_vPaths 是这样声明的(在我的班级中):
std::vector<std::string> m_vPaths;
回答by sth
The erase()method returns a new (valid) iterator that points to the next element after the deleted one. You can use this iterator to continue with the loop:
该erase()方法返回一个新的(有效的)迭代器,它指向被删除元素之后的下一个元素。您可以使用此迭代器继续循环:
std::vector<std::string>::iterator iter;
for (iter = m_vPaths.begin(); iter != m_vPaths.end(); ) {
if (::DeleteFile(iter->c_str()))
iter = m_vPaths.erase(iter);
else
++iter;
}
回答by GManNickG
Check out std::remove_if:
#include <algorithm> // for remove_if
#include <functional> // for unary_function
struct delete_file : public std::unary_function<const std::string&, bool>
{
bool operator()(const std::string& strPath) const
{
return ::DeleteFile(strPath.c_str());
}
}
m_vPaths.erase(std::remove_if(m_vPaths.begin(), m_vPaths.end(), delete_file()),
m_vPaths.end());
Use a std::listto stop the invalid iterators problem, though you lose random access. (And cache performance, in general)
使用 astd::list来停止无效迭代器问题,尽管您失去了随机访问。(和缓存性能,一般)
For the record, the way you would implement your code would be:
作为记录,您实现代码的方式是:
typedef std::vector<std::string> string_vector;
typedef std::vector<std::string>::iterator string_vector_iterator;
string_vector_iterator iter = m_vPaths.begin();
while (iter != m_vPaths.end())
{
if(::DeleteFile(iter->c_str()))
{
// erase returns the new iterator
iter = m_vPaths.erase(iter);
}
else
{
++iter;
}
}
But you should use std::remove_if(reinventing the wheel is bad).
但是你应该使用std::remove_if(重新发明轮子是不好的)。
回答by Jerry Coffin
Given the time to erase a file, it probably doesn't matter, but I'd still advise iterating through the vector backwards -- that way you're normally deleting items from (close to) the end of the vector. The time taken to delete an item is proportional to the number of items following it in the vector. If (for example) you have a vector of 100 file names, and you successfully delete all of them, you'll copy the last element 100 times in the process (and copy the second to last element 99 times, and so on).
考虑到擦除文件的时间,这可能无关紧要,但我仍然建议向后遍历向量 - 这样您通常会从(接近)向量末尾删除项目。删除一个项目所花费的时间与向量中跟随它的项目数量成正比。如果(例如)您有一个包含 100 个文件名的向量,并且您成功删除了所有文件名,那么您将在此过程中复制最后一个元素 100 次(并复制倒数第二个元素 99 次,以此类推)。
OTOH, if you start from the end and work backwards, you don't copy as long as deleting the files is successful. You can use reverse iterators to traverse the vector backwards without changing much of anything else. For example, GMan's code using remove_if should continue to work (only a tad faster) simply by substituting rbegin() for begin(), and rend() for end.
OTOH,如果你从头开始向后工作,只要删除文件成功,你就不会复制。您可以使用反向迭代器向后遍历向量,而无需更改任何其他内容。例如,GMan 使用 remove_if 的代码应该可以继续工作(只是快一点),只需用 rbegin() 代替 begin(),用 rend() 代替 end。
Another possibility is to use a deque instead of a vector -- a deque can erase items from the end orthe beginning of the collection in constant time.
另一种可能性是使用双端队列而不是向量——双端队列可以在恒定时间内从集合的末尾或开头擦除项目。

