2010-06-16 8 views
0
#include "stdafx.h" 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    string s = "Haven't got an idea why."; 
    auto beg = s.begin(); 
    auto end = s.end(); 
    while (beg < end) 
    { 
     cout << *beg << '\n'; 
     if (*beg == 'a') 
     {//whithout if construct it works perfectly 
      beg = s.erase(beg); 
     } 
     ++beg; 
    } 
    return 0; 
} 

Pourquoi si j'efface un ou plusieurs caractères de cette chaîne, ce code se casse? Je suppose que cela a quelque chose à voir avec l'itérateur retourné après que l'opération d'effacement ait été créée à une adresse plus élevée que l'itérateur final, mais je ne suis pas sûr et ce n'est sûrement pas un bon comportement. Ou est-ce?Comportement d'itérateur étrange

+0

Il est probablement plus facile d'effacer les caractères de 'end()' à begin() ', puisque' begin() 'ne changera pas lors de l'effacement des caractères. – MSalters

Répondre

8

Il existe plusieurs problèmes avec ce code.

  1. Ne mettez pas en cache la valeur s.end(); il change lorsque vous supprimez des éléments. Ne pas utiliser beg < end. L'approche idiomatique consiste à écrire beg != end. Si vous essayez d'itérer passé end, le résultat est indéfini et une version de débogage de la bibliothèque de chaînes peut planter délibérément votre processus, il est donc inutile d'utiliser <.
  2. L'itérateur renvoyé à partir de s.erase(beg) peut être s.end(), auquel cas ++beg dépasse la fin.

est ici un (je pense) version correcte:

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    string s = "Haven't got an idea why."; 
    for (auto beg = s.begin(); beg != s.end();) 
    { 
     cout << *beg << '\n'; 
     if (*beg == 'a') 
     {//whithout if construct it works perfectly 
      beg = s.erase(beg); 
     } 
     else 
     { 
      ++beg; 
     } 
    } 
} 

EDIT: Je suggère d'accepter la réponse de FredOverflow. C'est plus simple et plus rapide que ce qui précède.

+0

"Ne mettez pas en cache la valeur de s.end(), elle change lorsque vous supprimez des éléments." Un de mes principaux problèmes avec 'auto'. Regardez 'auto end = s.end()' et dites-moi que cela n'est pas trompeur pour les nouveaux arrivants! [commentaire général; ne se référant pas spécifiquement à cet OP] –

+0

@Tomalak: Je suis d'accord que la signification non auto-évidente de 'auto' est malheureuse, mais suggérez-vous que le choix d'utiliser' auto' était incorrect? –

+0

@Marcelo: Pas du tout! –

4

La valeur s.end() stockée dans end est invalide après s.erase(). Par conséquent, ne l'utilisez pas.

+0

Je suis sûr que vous avez raison, mais ça n'a pas beaucoup de sens, n'est-ce pas? Pourquoi l'itérateur de fin serait-il invalidé? –

+1

@ A-ha: Il est invalidé par définition. La norme le dit. –

+1

@ A-ha Ce que la norme dit (comme Marcelo l'a souligné à juste titre) a une bonne raison de l'être. L'itérateur regardait la position finale de votre chaîne et vous en supprimiez un caractère. Il est seulement logique que sa valeur précédente ne soit plus valide. Si vous voulez vérifier la nouvelle fin, vous devez demander à nouveau au conteneur (s.end()), n'utilisez pas une valeur précédemment mise en cache. –

1

Notez la sémantique d'un basic_string et de ses itérateurs.

De www.ski.com/tech/stl

Notez également que, selon la norme C de, basic_string a très inhabituelle sémantique iterator invalidation. Les itérateurs peuvent être invalidés par l'échange, la réservation, l'insertion et l'effacement (et par des fonctions équivalentes à insérer et/ou effacer, telles que effacer, redimensionner, ajouter et remplacer). De plus, cependant, le premier appel à toute fonction membre non-const, y compris la version non-const de begin() ou operator [], peut invalider les itérateurs. (Le but de ces règles d'invalidation itérateur est de donner implementors une plus grande liberté dans les techniques de mise en œuvre.)

également ce qui se passe si

beg = s.erase(beg); 

Retourne un équivalent iterator à la fin()

6

éléments Effacer un par un à partir de vecteurs ou de chaînes a une complexité quadratique. Il existe de meilleures solutions avec une complexité linéaire:

#include <string> 
#include <algorithm> 

int main() 
{ 
    std::string s = "Haven't got an idea why."; 
    s.erase(std::remove(s.begin(), s.end(), 'a'), s.end()); 
    std::cout << s << std::endl; 
} 
1

Lors d'une opération d'effacement d'appel, le pointeur d'itérateur de fin mémorisé devient invalide. Donc, utilisez la fonction s.end() dans la condition de boucle while

0

Vous devez effectuer une itération de .end() - 1 à .begin(). Dans le même temps, il n'est pas prudent d'utiliser des opérateurs de comparaison autres que == et! =.

Voici mon code:

vector<long long> myVector (my, my+myCount); 
    //sort and iterate through top correlation data counts 
    sort (myVector.begin(), myVector.end()); 
    cout << endl; 
    int TopCorrelationDataCount = 0; 
    bool myVectorIterator_lastItem = false; 
    vector<long long>::iterator myVectorIterator=myVector.end()-1; 
    while (true) {      
     long long storedData = *myVectorIterator; 
     cout << TopCorrelationDataCount << " " << storedData << endl;      

     //prepare for next item 
     TopCorrelationDataCount++; 
     //if (TopCorrelationDataCount >= this->TopCorrelationDataSize) break; 
     if (myVectorIterator_lastItem) break; 
     myVectorIterator--; 
     if (myVectorIterator==myVector.begin()) 
     { 
      myVectorIterator_lastItem = true; 
     } 
    } 

Note:. Il ne peut pas être fait au moyen d'un ordinaire, parce que vous devez savoir si == begin(). Si c'est le cas, ce sera votre dernière itération. Vous ne pouvez pas vérifier si ==. Begin() - 1, car cela entraînera une erreur d'exécution.

Si vous souhaitez uniquement utiliser des éléments X dans un vecteur, utilisez TopCorrelationDataCount.