Pregunta

Tengo un std :: vector m_vPaths; Voy a iterar este vector y llamaré a: DeleteFile (strPath) a medida que avanzo. Si elimino correctamente el archivo, lo eliminaré del vector. Mi pregunta es, ¿puedo evitar tener que usar dos vectores? ¿Existe una estructura de datos diferente que pueda ser más adecuada para lo que necesito hacer?

ejemplo: el uso de iteradores casi hace lo que quiero, pero el problema es que una vez que se borra con un iterador, todos los iteradores se vuelven invá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 un 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;
¿Fue útil?

Solución

Vea 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 un std :: list para Detenga el problema de los iteradores no válidos, aunque pierda el acceso aleatorio. (Y el 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 debe usar std :: remove_if (reinventar la rueda es malo).

Otros consejos

El método erase () devuelve un nuevo iterador (válido) que apunta al siguiente elemento después del eliminado. Puede usar este iterador para continuar con el bucle:

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;
}

Dado el tiempo para borrar un archivo, es probable que no importe, pero aún así recomiendo iterar a través del vector hacia atrás, de esa manera normalmente se eliminan elementos del final del vector (cerca de). 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 archivos y los elimina con éxito, copiará el último elemento 100 veces en el proceso (y copiará el segundo al último elemento 99 veces, y así sucesivamente).

OTOH, si empiezas desde el final y trabajas hacia atrás, no copiarás mientras se borren los archivos. Puedes usar iteradores inversos para recorrer el vector hacia atrás sin cambiar mucho más. Por ejemplo, el código de GMan que usa remove_if debería continuar 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 al comienzo de la colección en un tiempo constante.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top