我有IInventory *的向量,我使用C ++ 11范围内,做的东西与每一个在列表中循环。
做一些东西,有一个,我可能想从列表中删除,并删除对象。 我知道我可以调用delete
上的指针任何时候把它清理干净,但在什么样的范围是从载体中删除它的正确方法,而for
循环? 如果我从列表中删除,将我的循环失效?
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
for (IInventory* index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
}
Answer 1:
不,你不能。 基于范围for
是当你需要一次访问一个容器中的每个元素。
您应该使用正常for
循环或它的堂兄弟之一,如果你需要修改的容器,当您去,访问超过一次的元素更多,或者通过容器中非线性的方式,否则迭代。
例如:
auto i = std::begin(inv);
while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}
Answer 2:
每一个元素从矢量删除的时候,你必须在或擦除元素之后承担的迭代器不再有效,因为每个后续擦除元素中的元素都被移动。
一系列基于for循环是“正常的”使用循环迭代只是语法糖,因此上述适用。
话虽这么说,你可以简单地:
inv.erase(
std::remove_if(
inv.begin(),
inv.end(),
[](IInventory* element) -> bool {
// Do "some stuff", then return true if element should be removed.
return true;
}
),
inv.end()
);
Answer 3:
您在遍历它非常不应该修改载体。 使用擦除remove惯用法。 如果你这样做,你很可能会遇到一些问题。 由于在vector
的erase
无效与元素开头的所有迭代器被擦除高达的end()
你需要确保你的迭代器的使用仍然有效:
for (MyVector::iterator b = v.begin(); b != v.end();) {
if (foo) {
b = v.erase( b ); // reseat iterator to a valid value post-erase
else {
++b;
}
}
请注意,你所需要的b != v.end()
测试原样。 如果你尝试如下优化它:
for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)
因为你的你会遇到UB e
在第一无效后erase
电话。
Answer 4:
它是一个严格要求删除,而元素在循环? 否则,你可以设置要删除为NULL,再拍传过来的矢量删除所有NULL指针的指针。
std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );
for ( IInventory* &index : inv )
{
// do some stuff
// ok I decided I need to remove this object from inv...?
if (do_delete_index)
{
delete index;
index = NULL;
}
}
std::remove(inv.begin(), inv.end(), NULL);
Answer 5:
遗憾的necroposting,也对不起,如果我的C ++的专长得到我的回答的方式,但如果你试图通过每个项目迭代,使可能发生的变化(如删除索引),请尝试使用backwords for循环。
for(int x=vector.getsize(); x>0; x--){
//do stuff
//erase index x
}
删除索引x时,下一个循环将是“前面的”最后一次迭代的项目。 我真的希望这有助于有人
Answer 6:
好吧,我来晚了,但不管怎么说:对不起,不正确的是我到目前为止阅读-这是可能的,你只需要两个迭代器:
std::vector<IInventory*>::iterator current = inv.begin();
for (IInventory* index : inv)
{
if(/* ... */)
{
delete index;
}
else
{
*current++ = index;
}
}
inv.erase(current, inv.end());
刚刚修改的值的迭代器指向并不能否定任何其他迭代器,所以我们可以做到这一点,而无需担心。 其实, std::remove_if
(GCC实现至少)做一些非常相似(使用经典的循环...),只是不删除任何东西,不会抹去。
请注意,但是,这不是线程安全的(!) - 但是,这个应用,也为上面的一些其他解决方案...
Answer 7:
我将与示例显示,下面的实例中删除从矢量奇数元素:
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 1
for(auto it = vecInt.begin();it != vecInt.end();){
if(*it % 2){// remove all the odds
it = vecInt.erase(it);
} else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 2
vecInt = {0, 1, 2, 3, 4, 5};
//method 2
for(auto it=std::begin(vecInt);it!=std::end(vecInt);){
if (*it % 2){
it = vecInt.erase(it);
}else{
++it;
}
}
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
// recreate vecInt, and use method 3
vecInt = {0, 1, 2, 3, 4, 5};
//method 3
vecInt.erase(std::remove_if(vecInt.begin(), vecInt.end(),
[](const int a){return a % 2;}),
vecInt.end());
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
输出AW如下:
024
024
024
请记住,该方法erase
将返回经过迭代器的下一个迭代。
从这里 ,我们可以用一个更产生方法:
template<class Container, class F>
void erase_where(Container& c, F&& f)
{
c.erase(std::remove_if(c.begin(), c.end(),std::forward<F>(f)),
c.end());
}
void test_del_vector(){
std::vector<int> vecInt{0, 1, 2, 3, 4, 5};
//method 4
auto is_odd = [](int x){return x % 2;};
erase_where(vecInt, is_odd);
// output all the remaining elements
for(auto const& it:vecInt)std::cout<<it;
std::cout<<std::endl;
}
看到这里,看看如何使用std::remove_if
。 https://en.cppreference.com/w/cpp/algorithm/remove
Answer 8:
一个更优雅的解决办法是切换到std::list
(假设你并不需要快速随机访问)。
list<Widget*> widgets ; // create and use this..
然后,您可以删除.remove_if
和某行的C ++函数对象:
widgets.remove_if( []( Widget*w ){ return w->isExpired() ; } ) ;
所以在这里我只是写一个接受一个参数(仿函数Widget*
)。 返回值是在其上删除的条件Widget*
从列表中。
我觉得这句法可口。 我不认为我会永远使用remove_if
为标准::向量 -有这么多inv.begin()
和inv.end()
噪声的存在,你可能会更好过使用整数索引基于删除或只是一个普通的旧规则基于迭代器删除(如下所示)。 但是,你应该不是真的被从中间取出std::vector
非常多,无论如何,所以切换到list
这种情况列表删除的频繁中间的建议。
不过请注意,我没有得到一个机会来调用delete
的Widget*
中删除的的。 要做到这一点,就应该是这样的:
widgets.remove_if( []( Widget*w ){
bool exp = w->isExpired() ;
if( exp ) delete w ; // delete the widget if it was expired
return exp ; // remove from widgets list if it was expired
} ) ;
你也可以使用常规的基于迭代器的循环,像这样:
// NO INCREMENT v
for( list<Widget*>::iterator iter = widgets.begin() ; iter != widgets.end() ; )
{
if( (*iter)->isExpired() )
{
delete( *iter ) ;
iter = widgets.erase( iter ) ; // _advances_ iter, so this loop is not infinite
}
else
++iter ;
}
如果你不喜欢的长度for( list<Widget*>::iterator iter = widgets.begin() ; ...
,你可以使用
for( auto iter = widgets.begin() ; ...
Answer 9:
我想我会做以下...
for (auto itr = inv.begin(); itr != inv.end();)
{
// Do some stuff
if (OK, I decided I need to remove this object from 'inv')
itr = inv.erase(itr);
else
++itr;
}
Answer 10:
在反对这种线程的标题,我会使用两个通道:
#include <algorithm>
#include <vector>
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
std::vector<IInventory*> toDelete;
for (IInventory* index : inv)
{
// Do some stuff
if (deleteConditionTrue)
{
toDelete.push_back(index);
}
}
for (IInventory* index : toDelete)
{
inv.erase(std::remove(inv.begin(), inv.end(), index), inv.end());
}
文章来源: Removing item from vector, while in C++11 range 'for' loop?