2009-10-22 6 views
48

J'ai un std :: vector m_vPaths; Je vais itérer ce vecteur et appeler :: DeleteFile (strPath) comme je vais. Si je supprime le fichier, je l'enlèverai du vecteur. Ma question est la suivante: est-ce que je peux avoir à utiliser deux vecteurs? Y a-t-il une structure de données différente qui pourrait mieux convenir à ce que je dois faire? L'utilisation des itérateurs fait presque ce que je veux, mais le problème est qu'une fois que vous avez effacé avec un itérateur, tous les itérateurs deviennent invalides. Par exemple: itérer vecteur, supprimer certains éléments que je vais

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 un problème, mais est-il une meilleure méthode plus efficace de faire ce que je suis en train de faire?

BTW, Incase on ne sait pas, m_vPaths est déclarée comme celui-ci (dans ma classe):

std::vector<std::string> m_vPaths; 
+0

En outre, je ne sais pas vraiment quel type de structure de données il utilise, s'il y a quelque chose de mieux qu'un vecteur me le faire savoir. Je ne pense pas que std :: queue ou std :: list a quelque chose qui va m'aider (je pourrais me tromper :) :) – cchampion

Répondre

67

Check out 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êter le problème itérateurs invalide, si vous perdez l'accès aléatoire. (Et les performances du cache, en général)


Pour mémoire, la façon dont vous mettre en œuvre 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 devez utiliser std::remove_if (réinvente la roue est mauvais).

90

La méthode erase() renvoie un nouvel itérateur (valide) qui pointe vers l'élément suivant après celui qui a été supprimé. Vous pouvez utiliser cette iterator pour continuer 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; 
} 
+2

Subtile. J'ai presque downvoted ceci parce que je pensais que l'effacement invaliderait les itérateurs suivants. Merci pour le lien. –

+0

Cependant, cette approche ne fonctionne pas pour tous les conteneurs STL (en particulier std :: map), tandis que la combinaison erase (range)/remove fonctionne avec n'importe quel conteneur STL. –

+3

Pour 'std :: map', vous pouvez utiliser' erase (iter ++); 'car' erase' n'invalide pas d'autres itérateurs que celui effacé. – sth

7

Compte tenu du temps d'effacer un fichier, il n'a probablement pas, mais je serais encore Conseilleriez itérer le vecteur arrière - de cette façon vous » En règle générale, supprimez les éléments de (proche de) la fin du vecteur. Le temps nécessaire 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 fichiers et que vous les effacez tous avec succès, vous copiez le dernier élément 100 fois dans le processus (et copiez l'avant-dernier élément 99 fois, et ainsi de suite). OTOH, si vous commencez à partir de la fin et travaillez en arrière, vous ne copiez pas tant que la suppression des fichiers est réussie. Vous pouvez utiliser des itérateurs inversés pour parcourir le vecteur en arrière sans rien changer d'autre. 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é est d'utiliser un deque au lieu d'un vecteur - une deque peut effacer les éléments de la fin ou le début de la collection en temps constant.

+0

Ne vaudrait-il pas mieux déplacer le nième bon item au nième indice? Nous avons juste besoin d'indexer la valeur de l'emplacement, une pour le nième bon emplacement et une pour le nième indice. Cela permettra de s'assurer que tous les bons éléments avancent, et une fois cela fait, nous pouvons redimensionner le vecteur à n éléments (pas sûr si le support). Tout au plus, n-1 swap se produit lorsque le premier élément doit être retiré. – saurabheights