Tengo un vector de IInventory *, y estoy recorriendo la lista usando el rango C ++ 11 para, para hacer cosas con cada uno.
Después de hacer algunas cosas con uno, es posible que desee eliminarlo de la lista y eliminar el objeto. Sé que puedo llamar delete
al puntero en cualquier momento para limpiarlo, pero ¿cuál es la forma correcta de eliminarlo del vector, mientras está en el for
ciclo de rango ? Y si lo elimino de la lista, ¿se invalidará mi bucle?
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'...
}
std::remove_if
con un predicado que "hace cosas" y luego devuelve verdadero si desea eliminar el elemento.std::list
abajoRespuestas:
No, no puedes. Basado en rango
for
es para cuando necesita acceder a cada elemento de un contenedor una vez.Debe usar el
for
ciclo 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 otra manera de manera no lineal a través del contenedor.Por ejemplo:
fuente
true
, AFAIU, y parece mejor de esta manera no mezclar la lógica de iteración con el predicado.remove_if
es mejor.erase
devuelve un nuevo iterador válido. Puede que no sea eficiente, pero está garantizado que funcionará.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 siguen al elemento borrado se mueve.
Un bucle for basado en rangos es simplemente azúcar sintáctico para un bucle "normal" que usa iteradores, por lo que se aplica lo anterior.
Dicho esto, simplemente podría:
fuente
vector
nunca se reasignará debido a una llamada aerase
. La razón por la que los iteradores se invalidan es porque cada uno de los elementos que siguen al elemento borrado se mueve.[&]
sería apropiado, para permitirle "hacer algunas cosas" con variables locales.remove_if
con.erase
, de lo contrario no pasa nada.std::remove_if
Está encendido).remove_if
debe hacerse internamente). Sin embargo, si usted tiene unavector
de 5 elementos y sólo.erase()
1 a la vez, no hay impacto en el rendimiento para el uso de iteradores vsremove_if
. Si la lista es más grande, debería cambiar astd::list
donde hay mucha eliminación de la mitad de la lista.Lo ideal es que no modifiques el vector mientras iteras sobre él. Utilice el modismo borrar-eliminar. Si lo hace, es probable que encuentre algunos problemas. Dado que en
vector
anerase
invalida todos los iteradores comenzando con el elemento que se borra hasta elend()
, deberá asegurarse de que sus iteradores sigan siendo válidos utilizando:Tenga en cuenta que necesita la
b != v.end()
prueba tal cual. Si intenta optimizarlo de la siguiente manera:se encontrará con UB, ya que
e
se invalidará después de la primeraerase
llamada.fuente
std::remove
, y es O (N ^ 2) no O (N).¿Es un requisito estricto eliminar elementos mientras se 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.
fuente
lo siento por necropostar y también lo siento si mi experiencia en c ++ se interpone en el camino de mi respuesta, pero si intenta iterar a través de cada elemento y hacer posibles cambios (como borrar un índice), intente usar un bucle de contraseñas.
al borrar el índice x, el siguiente ciclo será para el elemento "delante de" la última iteración. realmente espero que esto haya ayudado a alguien
fuente
Está bien, llego tarde, pero de todos modos: lo siento, no corrijo lo que leí hasta ahora; es posible, solo necesitas dos iteradores:
La simple modificación del valor al que apunta un iterador no invalida ningún otro iterador, por lo que podemos hacer esto sin tener que preocuparnos. Realmente,
std::remove_if
(la implementación de gcc al menos) hace algo muy similar (usando un bucle clásico ...), simplemente no borra nada y no borra.Sin embargo, tenga en cuenta que esto no es seguro para subprocesos (!); Sin embargo, esto también se aplica a algunas de las otras soluciones anteriores ...
fuente
erase
(siempre que elimine más de un elemento, por supuesto)?Mostraré con un ejemplo, el siguiente ejemplo elimina elementos impares del vector:
salida aw a continuación:
Tenga en cuenta que el método
erase
devolverá el siguiente iterador del iterador pasado.Desde aquí , podemos usar un método más generar:
Vea aquí para ver cómo se usa
std::remove_if
. https://en.cppreference.com/w/cpp/algorithm/removefuente
En oposición a este título de hilos, usaría dos pases:
fuente
Una solución mucho más elegante sería cambiar a
std::list
(asumiendo que no necesita un acceso aleatorio rápido).Luego puede eliminar con
.remove_if
y un functor de C ++ en una línea:Así que aquí solo estoy escribiendo un functor que acepta un argumento (el
Widget*
). El valor de retorno es la condición en la que eliminar unWidget*
de la lista.Encuentro esta sintaxis agradable. No creo que lo use nunca
remove_if
para std :: vectors ; hay tantoinv.begin()
yinv.end()
ruido allí, probablemente sea mejor que use una eliminación basada en índices enteros o simplemente una eliminación basada en iteradores regulares antiguos (como se muestra abajo). Pero destd::vector
todos modos no debería eliminar de la mitad de un mucho, por lo quelist
se recomienda cambiar a una 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
delete
a losWidget*
que se eliminaron. Para hacer eso, se vería así:También puede usar un bucle regular basado en iteradores como este:
Si no le gusta la longitud de
for( list<Widget*>::iterator iter = widgets.begin() ; ...
, puede usarfuente
remove_if
en unastd::vector
realidad de trabajo, y cómo se mantiene la complejidad de O (N).std::vector
siempre va a deslizar cada elemento después del que quitó hacia arriba, haciendo unastd::list
elección mucho mejor.remove_if
deslizará cada elemento hacia arriba según el número de espacios liberados. Para cuando tenga en cuenta el uso de la caché,remove_if
esstd::vector
probable que supere la eliminación de unstd::list
. Y conservaO(1)
el acceso aleatorio.Creo que haría lo siguiente ...
fuente
no puede eliminar el iterador durante la iteración del bucle porque el recuento de iteradores no coincide y después de alguna iteración tendría un iterador no válido.
Solución: 1) tomar la copia del vector original 2) iterar el iterador usando esta copia 2) hacer algunas cosas y eliminarlo del vector original.
fuente
Borrar elementos uno por uno conduce fácilmente a un rendimiento N ^ 2. Es mejor marcar los elementos que deben borrarse y borrarlos inmediatamente después del bucle. Si puedo suponer nullptr en un elemento no válido en su vector, entonces
Deberia trabajar.
En caso de que su "Hacer algunas cosas" no cambie elementos del vector y solo se use para tomar la decisión de eliminar o mantener el elemento, puede convertirlo a lambda (como se sugirió en la publicación anterior de alguien) y usar
fuente