list iterator c++
iterar vector, eliminar ciertos elementos a medida que avanzo (3)
Echa un vistazo a std::remove_if
:
#include <algorithm> // for remove_if
#include <functional> // for unary_function
struct delete_file : public std::unary_function<const std::string&, bool>
{
bool operator()(const std::string& strPath) const
{
return ::DeleteFile(strPath.c_str());
}
}
m_vPaths.erase(std::remove_if(m_vPaths.begin(), m_vPaths.end(), delete_file()),
m_vPaths.end());
Use std::list
para detener el problema de los iteradores no válidos, aunque pierda el acceso aleatorio. (Y rendimiento del caché, en general)
Para el registro, la forma en que implementaría su código sería:
typedef std::vector<std::string> string_vector;
typedef std::vector<std::string>::iterator string_vector_iterator;
string_vector_iterator iter = m_vPaths.begin();
while (iter != m_vPaths.end())
{
if(::DeleteFile(iter->c_str()))
{
// erase returns the new iterator
iter = m_vPaths.erase(iter);
}
else
{
++iter;
}
}
Pero deberías usar std::remove_if
(reinventar la rueda es malo).
Tengo un std :: vector m_vPaths; Voy a iterar este vector y llamar :: DeleteFile (strPath) sobre la marcha. Si borro el archivo con éxito, lo eliminaré del vector. Mi pregunta es ¿puedo evitar tener que usar dos vectores? ¿Hay una estructura de datos diferente que pueda ser más adecuada para lo que tengo que hacer?
ejemplo: el uso de iteradores casi hace lo que quiero, pero el problema es que una vez que se borra el uso de un iterador, todos los iteradores dejan de ser válidos.
std::vector<std::string> iter = m_vPaths.begin();
for( ; iter != m_vPaths.end(); iter++) {
std::string strPath = *iter;
if(::DeleteFile(strPath.c_str())) {
m_vPaths.erase(iter);
//Now my interators are invalid because I used erase,
//but I want to continue deleteing the files remaining in my vector.
}
}
Puedo usar dos vectores y ya no tendré ningún problema, pero ¿hay algún método mejor y más eficiente para hacer lo que estoy tratando de hacer?
Por cierto, en caso de que no esté claro, m_vPaths se declara así (en mi clase):
std::vector<std::string> m_vPaths;
El método erase()
devuelve un iterador nuevo (válido) que apunta al siguiente elemento después del eliminado. Puede usar este iterador para continuar con el ciclo:
std::vector<std::string>::iterator iter;
for (iter = m_vPaths.begin(); iter != m_vPaths.end(); ) {
if (::DeleteFile(iter->c_str()))
iter = m_vPaths.erase(iter);
else
++iter;
}
Teniendo en cuenta el tiempo para borrar un archivo, probablemente no importe, pero aún aconsejo iterar a través del vector al revés, de esa manera normalmente está eliminando elementos de (cerca de) el final del vector. El tiempo necesario para eliminar un elemento es proporcional al número de elementos que lo siguen en el vector. Si (por ejemplo) tiene un vector de 100 nombres de archivo y los elimina con éxito, copiará el último elemento 100 veces en el proceso (y copiará el penúltimo elemento 99 veces, y así sucesivamente).
OTOH, si comienzas desde el final y trabajas al revés, no copias mientras la eliminación de los archivos sea exitosa. Puede usar iteradores inversos para atravesar el vector hacia atrás sin cambiar mucho más. Por ejemplo, el código de GMan que usa remove_if debería seguir funcionando (solo un poco más rápido) simplemente sustituyendo rbegin () por begin () y rend () por end.
Otra posibilidad es usar un deque en lugar de un vector: un deque puede borrar elementos del final o del comienzo de la colección en tiempo constante.