programas - ¿Quitar el elemento del vector, mientras que en el rango de C++ 11 ''for'' loop?
programas en c++ ejemplos avanzados (8)
¿Es un requisito estricto eliminar elementos mientras está en ese bucle? De lo contrario, podría establecer los punteros que desea eliminar en NULL y hacer otra pasada sobre el vector para eliminar todos los punteros 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);
Tengo un vector de IInventory *, y estoy recorriendo la lista usando el rango de C ++ 11, para hacer cosas con cada uno.
Después de hacer algunas cosas con una, es posible que desee eliminarlo de la lista y eliminar el objeto. Sé que puedo llamar delete
en el puntero en cualquier momento para limpiarlo, pero ¿cuál es la forma correcta de eliminarlo del vector, mientras estoy en el rango for
ciclo? Y si lo elimino de la lista ¿mi bucle será invalidado?
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''...
}
Cada vez que se elimina un elemento del vector, debe asumir que los iteradores en o después del elemento borrado ya no son válidos, porque cada uno de los elementos que suceden al elemento borrado se mueve.
Un bucle for basado en el rango es solo azúcar sintáctica para bucle "normal" usando iteradores, por lo que se aplica lo anterior.
Dicho esto, podrías simplemente:
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()
);
Creo que haría lo siguiente ...
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;
}
Lo ideal es que no modifiques el vector mientras lo iteras. Usa el idioma borrar-eliminar. Si lo haces, es probable que encuentres algunos problemas. Dado que en un vector
un erase
invalida todos los iteradores que comiencen con el elemento borrado hasta el end()
, deberá asegurarse de que sus iteradores sigan siendo válidos mediante:
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;
}
}
Tenga en cuenta que necesita la prueba b != v.end()
tal como está. Si intenta optimizarlo de la siguiente manera:
for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)
se encontrará con UB ya que su e
se invalida después de la primera llamada de erase
.
No, no puedes. El rango for
es para cuando necesita acceder a cada elemento de un contenedor una vez.
Debería usar el ciclo for
normal o uno de sus primos si necesita modificar el contenedor a medida que avanza, acceder a un elemento más de una vez o iterar de forma no lineal a través del contenedor.
Por ejemplo:
auto i = std::begin(inv);
while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}
OK, llegué tarde, pero de todos modos: Lo siento, no corrijo lo que leí hasta ahora: es posible, solo necesitas dos iteradores:
std::vector<IInventory*>::iterator current = inv.begin();
for (IInventory* index : inv)
{
if(/* ... */)
{
delete index;
}
else
{
*current++ = index;
}
}
inv.erase(current, inv.end());
Solo modificar el valor al que apunta un iterador no invalida ningún otro iterador, por lo que podemos hacerlo sin tener que preocuparnos. En realidad, std::remove_if
(al menos la implementación de gcc) hace algo muy similar (usando un loop clásico ...), simplemente no borra nada y no borra.
Tenga en cuenta, sin embargo, que esto no es seguro para subprocesos (!); Sin embargo, esto también se aplica a algunas de las otras soluciones anteriores ...
Una solución mucho más elegante sería cambiar a std::list
(suponiendo que no necesita acceso aleatorio rápido).
list<Widget*> widgets ; // create and use this..
Luego puede eliminar con .remove_if
y un functor C ++ en una línea:
widgets.remove_if( []( Widget*w ){ return w->isExpired() ; } ) ;
Así que aquí estoy escribiendo un functor que acepta un argumento (el Widget*
). El valor de retorno es la condición para eliminar un Widget*
de la lista.
Encuentro que esta sintaxis es aceptable. No creo que alguna vez use remove_if
para std::vectors - hay tanto inv.begin()
y inv.end()
ruido que probablemente sea mejor usar una eliminación basada en índices enteros o simplemente una simple eliminación regular basada en un iterador (como se muestra a continuación). Pero en realidad no deberías estar eliminando de la mitad de un std::vector
mucho de todos modos, así que se recomienda cambiar a una list
para este caso de eliminación frecuente de la mitad de la lista.
Sin embargo, tenga en cuenta que no tuve la oportunidad de llamar a delete
en los Widget*
que se eliminaron. Para hacer eso, se vería así:
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
} ) ;
También podría usar un bucle regular basado en iteradores como ese:
// 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 ;
}
Si no le gusta la longitud de for( list<Widget*>::iterator iter = widgets.begin() ; ...
, puede usar
for( auto iter = widgets.begin() ; ...
lo siento por necroposting y también lo siento si mi experiencia en c ++ se interpone en el camino de mi respuesta, pero si tratas de iterar a través de cada elemento y hacer posibles cambios (como borrar un índice), intenta usar un backwords for loop.
for(int x=vector.getsize(); x>0; x--){
//do stuff
//erase index x
}
al borrar el índice x, el siguiente ciclo será para el ítem "al frente" de la última iteración. realmente espero que esto haya ayudado a alguien