library - list c++> stl
¿Se pueden eliminar elementos de una lista estándar:: iterando a través de ella? (11)
Creo que tienes un error, codifico de esta manera:
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);
}
Tengo un código que se ve así:
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);
Me gustaría eliminar los elementos inactivos inmediatamente después de actualizarlos, para evitar volver a recorrer la lista. Pero si agrego las líneas comentadas, recibo un error cuando llego a i++
: "Listar iterador no incrementable". Probé algunas alternativas que no aumentaron en la declaración for, pero no conseguí que nada funcionara.
¿Cuál es la mejor manera de eliminar elementos mientras recorres una lista de std ::?
Debe incrementar el iterador primero (con i ++) y luego eliminar el elemento anterior (por ejemplo, utilizando el valor devuelto de i ++). Puedes cambiar el código a un bucle de tiempo de esta manera:
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;
}
}
Este es un ejemplo que utiliza un bucle for
que itera la lista e incrementa o revalida el iterador en el caso de que se elimine un elemento durante el recorrido de la lista.
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);
He resumido, aquí está el método de tres con el ejemplo:
1. usando while
loop
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. usando la remove_if
miembro remove_if
en la lista:
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. usando la función std::remove_if
combina con la función de erase
miembro:
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. usando for
bucle, debe tener en cuenta actualizar el iterador:
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
La alternativa para la versión en bucle a la respuesta de Kristo.
Pierde algo de eficiencia, retrocede y luego vuelve a avanzar al eliminar, pero a cambio del incremento adicional del iterador, puede hacer que el iterador se declare en el ámbito del bucle y que el código parezca un poco más limpio. Qué elegir depende de las prioridades del momento.
La respuesta fue totalmente fuera de tiempo, lo sé ...
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);
}
}
La eliminación invalida solo los iteradores que apuntan a los elementos que se eliminan.
Entonces, en este caso, después de eliminar * i, i se invalida y no se puede hacer un incremento.
Lo que puede hacer es primero guardar el iterador del elemento que se va a eliminar, luego incrementar el iterador y luego eliminar el guardado.
Necesitas hacer la combinación de la respuesta de Kristo y la de 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 != items.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);
}
}
Por supuesto, lo más eficiente y sabio de SuperCool® STL sería algo como esto:
// 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)));
Puedes escribir
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++;
}
}
Puede escribir código equivalente con std::list::remove_if
, que es menos detallado y más explícito
items.remove_if([] (item*i) {
bool isActive = (*i)->update();
if (!isActive)
return true;
other_code_involving(*i);
return false;
});
El idioma std::vector::erase
std::remove_if
debe usar cuando los elementos son un vector en lugar de una lista para mantener la compexidad en O (n), o en caso de que escriba un código genérico, los elementos pueden ser un contenedor sin efecto. forma de borrar elementos individuales (como un vector)
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;
}));
Quieres hacer:
i= items.erase(i);
Eso actualizará correctamente el iterador para que apunte a la ubicación después del iterador que eliminó.
Si piensa en std::list
como en una cola, entonces puede poner en cola y en cola todos los elementos que desea conservar, pero solo en cola (y no en cola) el elemento que desea eliminar. Aquí hay un ejemplo donde quiero eliminar 5 de una lista que contiene los números 1-10 ...
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
ahora solo tendrá los números 1-4 y 6-10.
Utilice el algoritmo std :: remove_if.
Edición: El trabajo con colecciones debe ser como: 1. preparar colección. 2. Proceso de recogida.
La vida será más fácil si no se mezclan estos pasos.
- std :: remove_if. o list :: remove_if (si sabe que trabaja con list y no con TCollection)
- std :: for_each