Question

J'ai un std :: vector m_vPaths; Je vais itérer ce vecteur et appeler :: DeleteFile (strPath) au fur et à mesure. Si je réussis à supprimer le fichier, je le supprimerai du vecteur. Ma question est la suivante: puis-je éviter d'utiliser deux vecteurs? Existe-t-il une structure de données différente qui conviendrait mieux à ce que je dois faire?

exemple: utiliser des itérateurs fait presque ce que je veux, mais le problème est qu'une fois que vous effacez à l'aide d'un itérateur, tous les itérateurs deviennent invalides.

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

Je peux utiliser deux vecteurs et je n'aurai plus de problème, mais existe-t-il une méthode plus efficace et plus efficace pour faire ce que j'essaie de faire?

d'ailleurs, en cas de doute, m_vPaths est déclaré comme ceci (dans ma classe):

std::vector<std::string> m_vPaths;
Était-ce utile?

La solution

Découvrez 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());

Utilisez un std :: list pour arrêtez le problème des itérateurs non valides, bien que vous perdiez l'accès aléatoire. (Et les performances du cache, en général)

Pour mémoire, la manière dont vous implémenteriez votre code serait:

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

Mais vous devriez utiliser std :: remove_if (réinventer la roue est une mauvaise chose).

Autres conseils

La méthode erase () renvoie un nouvel itérateur (valide) qui pointe vers l'élément suivant après l'élément supprimé. Vous pouvez utiliser cet itérateur pour continuer avec la boucle:

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

Étant donné le temps nécessaire pour effacer un fichier, cela n'a probablement aucune importance, mais je vous conseillerais tout de même d'effectuer une itération inverse du vecteur - de cette façon, vous supprimez normalement les éléments situés à la fin du vecteur. Le temps pris pour supprimer un élément est proportionnel au nombre d'éléments qui le suivent dans le vecteur. Si (par exemple) vous avez un vecteur de 100 noms de fichier et que vous les supprimez tous avec succès, vous copiez le dernier élément 100 fois dans le processus (et copiez l'avant-dernier élément 99 fois, etc.).

OTOH, si vous commencez par la fin et travaillez en arrière, vous ne copiez pas tant que la suppression des fichiers a réussi. Vous pouvez utiliser des itérateurs inversés pour parcourir le vecteur en arrière sans changer grand-chose. Par exemple, le code de GMan utilisant remove_if devrait continuer à fonctionner (seulement un peu plus vite) en remplaçant simplement rbegin () par begin () et rend () par end.

Une autre possibilité consiste à utiliser un deque au lieu d'un vecteur - un deque peut effacer les éléments à partir de la fin ou du début de la collection en temps constant.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top