72 votes

itérer le vecteur, supprimer certains éléments au fur et à mesure

J'ai un std::vector m_vPaths; je vais itérer ce vecteur et de les appeler ::DeleteFile(strPath) que je vais. Si j'ai réussi à supprimer le fichier, je vais l'enlever à partir du vecteur. Ma question est puis-je obtenir autour avoir à utiliser deux vecteurs? Est-il différent de la structure de données qui pourrait être mieux adapté pour ce que je dois faire?

exemple: en utilisant les itérateurs ne 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 de devenir invalide.

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

btw, au cas où il n'est pas clair, m_vPaths est déclaré comme ceci (dans ma classe):

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

138voto

sth Points 91594

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

77voto

GManNickG Points 155079

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());

Utiliser un std::list pour arrêter les itérateurs invalides problème, même si vous perdez l'accès aléatoire. (Et les performances du cache, en général)


Pour l'enregistrement, la façon de 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éinventer la roue est mal).

7voto

Jerry Coffin Points 237758

Étant donné le temps pour effacer un fichier, il n'a probablement pas d'importance, mais j'aurais quand même conseiller de l'itération par le vecteur vers l'arrière -- de cette façon, vous êtes normalement en supprimant des éléments de (presque) la fin du vecteur. Le temps pris pour supprimer un objet est proportionnelle au nombre d'articles suivant dans le vecteur. Si (par exemple), vous disposez d'un vecteur de 100 noms de fichiers, et vous avez réussi à supprimer tous, vous allez copier le dernier élément de 100 fois dans le processus (et la copie de l'avant-dernier élément de 99 fois, et ainsi de suite).

Otoh, que, si vous commencez à partir de la fin et de travailler à l'envers, vous ne copiez pas aussi longtemps que la suppression de ces fichiers est réussie. Vous pouvez utiliser le reverse des itérateurs pour parcourir le vecteur vers l'arrière sans changer quoi que ce soit d'autre. Par exemple, GMan du code à l'aide de remove_if devrait continuer à travailler (seulement un peu plus vite) simplement en substituant rbegin() pour commencer(), et rené() pour la fin.

Une autre possibilité est d'utiliser un deque au lieu d'un vecteur, d'un deque permet d'effacer des éléments à partir de la fin ou le début de la collecte en temps constant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X