提问人:AShelly 提问时间:2/28/2009 更新时间:4/25/2023 访问量:296398
您可以在遍历 std::list 时从 std::list 中删除元素吗?
Can you remove elements from a std::list while iterating through it?
问:
我的代码如下所示:
for (std::list<item*>::iterator i=items.begin();i!=items.end();i++)
{
bool isActive = (*i)->update();
//if (!isActive)
// items.remove(*i);
//else
other_code_involving(*i);
}
items.remove_if(CheckItemNotActive);
我想在更新后立即删除非活动项目,以避免再次浏览列表。但是,如果我添加注释掉的行,当我到达时会收到错误:“列表迭代器不可增量”。我尝试了一些在 for 语句中没有递增的替代方法,但我无法让任何东西起作用。i++
在浏览 std::list 时删除项目的最佳方法是什么?
答:
您必须先递增迭代器(使用 i++),然后删除前一个元素(例如,通过使用 i++ 的返回值)。您可以将代码更改为 while 循环,如下所示:
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i++); // alternatively, i = items.erase(i);
}
else
{
other_code_involving(*i);
++i;
}
}
评论
i = items.erase(i)
您要执行以下操作:
i= items.erase(i);
这将正确地更新迭代器,使其指向您删除的迭代器之后的位置。
评论
i==items.begin()
i= items.erase(i);
使用算法。std::remove_if
编辑:使用集合应该是这样的:
- 准备收集。
- 进程收集。
如果你不混合这些步骤,生活会更容易。
std::remove_if
.或者 ( 如果您知道您使用的是 list 而不是list::remove_if
TCollection
)std::for_each
评论
删除仅使指向被删除元素的迭代器失效。
因此,在这种情况下,删除 *i 后,i 无效,您无法对其进行增量。
你可以做的是先保存要删除的元素的迭代器,然后递增迭代器,然后删除保存的迭代器。
评论
您需要将 Kristo 的答案和 MSN 的答案结合起来:
// Note: Using the pre-increment operator is preferred for iterators because
// there can be a performance gain.
//
// Note: As long as you are iterating from beginning to end, without inserting
// along the way you can safely save end once; otherwise get it at the
// top of each loop.
std::list< item * >::iterator iter = items.begin();
std::list< item * >::iterator end = items.end();
while (iter != end)
{
item * pItem = *iter;
if (pItem->update() == true)
{
other_code_involving(pItem);
++iter;
}
else
{
// BTW, who is deleting pItem, a.k.a. (*iter)?
iter = items.erase(iter);
}
}
当然,最高效、最酷®的 STL 精明是这样的:
// This implementation of update executes other_code_involving(Item *) if
// this instance needs updating.
//
// This method returns true if this still needs future updates.
//
bool Item::update(void)
{
if (m_needsUpdates == true)
{
m_needsUpdates = other_code_involving(this);
}
return (m_needsUpdates);
}
// This call does everything the previous loop did!!! (Including the fact
// that it isn't deleting the items that are erased!)
items.remove_if(std::not1(std::mem_fun(&Item::update)));
评论
std::not1
std::mem_fun
克里斯托答案的循环版本的替代方案。
你失去了一些效率,你在删除时会向后退,然后再次前进,但作为交换,你可以在循环范围内声明迭代器,让代码看起来更干净一些。选择什么取决于当下的优先事项。
答案完全不合时宜,我知道......
typedef std::list<item*>::iterator item_iterator;
for(item_iterator i = items.begin(); i != items.end(); ++i)
{
bool isActive = (*i)->update();
if (!isActive)
{
items.erase(i--);
}
else
{
other_code_involving(*i);
}
}
评论
iterator cannot be decremented
erase
random access iterator
forward only iterator
我认为你那里有一个错误,我是这样编码的:
for (std::list<CAudioChannel *>::iterator itAudioChannel = audioChannels.begin();
itAudioChannel != audioChannels.end(); )
{
CAudioChannel *audioChannel = *itAudioChannel;
std::list<CAudioChannel *>::iterator itCurrentAudioChannel = itAudioChannel;
itAudioChannel++;
if (audioChannel->destroyMe)
{
audioChannels.erase(itCurrentAudioChannel);
delete audioChannel;
continue;
}
audioChannel->Mix(outBuffer, numSamples);
}
评论
下面是一个使用循环的示例,该循环循环访问列表,并在遍历列表期间删除项时递增或重新验证迭代器。for
for(auto i = items.begin(); i != items.end();)
{
if(bool isActive = (*i)->update())
{
other_code_involving(*i);
++i;
}
else
{
i = items.erase(i);
}
}
items.remove_if(CheckItemNotActive);
如果您认为这是一个队列,那么您可以取消排队并排队所有要保留的项目,但只能取消排队(而不是排队)要删除的项目。下面是一个示例,我想从包含数字 1-10 的列表中删除 5...std::list
std::list<int> myList;
int size = myList.size(); // The size needs to be saved to iterate through the whole thing
for (int i = 0; i < size; ++i)
{
int val = myList.back()
myList.pop_back() // dequeue
if (val != 5)
{
myList.push_front(val) // enqueue if not 5
}
}
myList
现在只有数字 1-4 和 6-10。
评论
你可以写
std::list<item*>::iterator i = items.begin();
while (i != items.end())
{
bool isActive = (*i)->update();
if (!isActive) {
i = items.erase(i);
} else {
other_code_involving(*i);
i++;
}
}
您可以使用 编写等效代码,这样更不冗长且更明确std::list::remove_if
items.remove_if([] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
});
当 items 是向量而不是列表时,应该使用这个习惯语,以保持 O(n) 的compexity - 或者如果您编写通用代码并且 items 可能是一个容器,没有有效的方法来擦除单个项目(如向量)std::vector::erase
std::remove_if
items.erase(std::remove_if(begin(items), end(items), [] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
}));
我总结了一下,这里是三种方法的例子:
1.使用循环while
list<int> lst{4, 1, 2, 3, 5};
auto it = lst.begin();
while (it != lst.end()){
if((*it % 2) == 1){
it = lst.erase(it);// erase and go to next
} else{
++it; // go to next
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
2.在列表中使用成员功能:remove_if
list<int> lst{4, 1, 2, 3, 5};
lst.remove_if([](int a){return a % 2 == 1;});
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
3.结合会员功能使用:std::remove_if
erase
list<int> lst{4, 1, 2, 3, 5};
lst.erase(std::remove_if(lst.begin(), lst.end(), [](int a){
return a % 2 == 1;
}), lst.end());
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
4.使用循环时,应注意更新迭代器:for
list<int> lst{4, 1, 2, 3, 5};
for(auto it = lst.begin(); it != lst.end();++it){
if ((*it % 2) == 1){
it = lst.erase(it); erase and go to next(erase will return the next iterator)
--it; // as it will be add again in for, so we go back one step
}
}
for(auto it:lst)cout<<it<<" ";
cout<<endl; //4 2
评论
std::erase_if(lst, pred)
向后迭代可避免擦除要遍历的其余元素上的元素的影响:
typedef list<item*> list_t;
for ( list_t::iterator it = items.end() ; it != items.begin() ; ) {
--it;
bool remove = <determine whether to remove>
if ( remove ) {
items.erase( it );
}
}
PS:请参阅此,例如,关于向后迭代。
PS2:我没有彻底测试它是否能很好地处理末端的擦除元素。
评论
avoids the effect of erasing an element on the remaining elements
做 while 循环,它灵活、快速且易于读写。
auto textRegion = m_pdfTextRegions.begin();
while(textRegion != m_pdfTextRegions.end())
{
if ((*textRegion)->glyphs.empty())
{
m_pdfTextRegions.erase(textRegion);
textRegion = m_pdfTextRegions.begin();
}
else
textRegion++;
}
评论
我想分享我的方法。此方法还允许在迭代期间将元素插入到列表的后面
#include <iostream>
#include <list>
int main(int argc, char **argv) {
std::list<int> d;
for (int i = 0; i < 12; ++i) {
d.push_back(i);
}
auto it = d.begin();
int nelem = d.size(); // number of current elements
for (int ielem = 0; ielem < nelem; ++ielem) {
auto &i = *it;
if (i % 2 == 0) {
it = d.erase(it);
} else {
if (i % 3 == 0) {
d.push_back(3*i);
}
++it;
}
}
for (auto i : d) {
std::cout << i << ", ";
}
std::cout << std::endl;
// result should be: 1, 3, 5, 7, 9, 11, 9, 27,
return 0;
}
在 C++20 中,您可以使用erease_if:
std::erease_if(items, [](auto& i){
if (!i.update()) {
return true;
}
other_code_involving(i);
return false;
};
评论