Verward gebruik van c ++ STL iterator

Terwijl ik iterator als dit gebruik,

//include header files
using namespace std;

int main()
{
    map intIntMap;
    map::iterator pos;
    pos = intIntMap.begin();

    intIntMap[0] = 1;
    intIntMap[3] = 5;
    intIntMap[4] = 9;
    intIntMap[5] = 5;

    //遍历
    cout << (*pos).first << endl;

    while( pos != intIntMap.end() )
    {
        cout << pos->first << " <---> " << pos->second << endl;
        pos++;
    }

}

De uitvoer is 4;

Maar terwijl ik iterator als volgt gebruik:

//include header file
using namespace std;

int main()
{
    map intIntMap;
    map::iterator pos;

    intIntMap[0] = 1;
    intIntMap[3] = 5;
    intIntMap[4] = 9;
    intIntMap[5] = 5;

    //遍历
    pos = intIntMap.begin();
    cout << (*pos).first << endl;

    while( pos != intIntMap.end() )
    {
        cout << pos->first << " <---> " << pos->second << endl;
        pos++;
    }

}

De uitvoer is wat ik wil;

Ik wil weten wat het verschil is tussen het gebruik van iterator, wat gebeurde er met de eerste iterator toen ik nieuw sleutel/waarde-paar invoegde? Bedankt!

addtion: compileren is gebruik gcc 4.1.2, in meer verwarring, zoals dit:

compile is use gcc 4.1.2 , in feel more confused,like this :

4

5 antwoord

Omdat je begin() hebt aangeroepen toen de container leeg was, kreeg je een iterator die gelijk was aan end() (§23.1/7: "Als de container leeg is, begin dan met() == end() ").

Het invoegen van items in de container heeft hier niets aan veranderd, dus u heeft nog steeds pos == intIntMap.end() .

U voert vervolgens nul iteraties van uw lus uit, sinds pos == end() , en u voert de lus alleen uit zolang pos! = End() .

In het tweede voorbeeld stelt u pos() in nadat u de gegevens hebt ingevoegd, zodat u de eerste items in de verzameling krijgt en dit tot de laatste.

Bewerken: Voor zover het afdrukken van de inhoud van de kaart gaat, zou ik het waarschijnlijk meer als volgt doen:

std::ostream &operator<<(std::ostream &os, std::pair const &d) { 
    return os << d.first << " <---> " << d.second;
}

// ...

std::copy(intIntMap.begin(), intIntMap.end(), 
          std::ostream_iterator >(std::cout, "\n"));
10
toegevoegd

Toen ik dit met gdb doornam, ontdekte ik dat het eerste -veld van de iterator veranderde met elke toevoeging van een sleutel/waarde-paar. Het lijkt erop dat een iterator naar een kaart zonder gegevens erin (geretourneerd door begin() ) enige metadata bevat over de kaart zelf, met name de grootte van de kaart (de eerste het veld van de iterator is gegroeid met elke toevoeging van een sleutel/waarde-paar). Het aanroepen van begin() om de iterator op te halen nadat een sleutel/waarde-paar is toegevoegd, resulteert in het "verwachte" gedrag.

2
toegevoegd
... maar het geeft dat gedrag alleen per ongeluk, omdat het eerste ingevoegde element ook de kleinste index was. Wijzig de invoegvolgorde en uw resultaat zal veranderen. begin() geeft je een iterator naar wat het eerste item op de kaart was toen je het noemde . Voeg daar iets later aan toe en uw iterator zal niet langer verwijzen naar het eerste element.
toegevoegd de auteur Jerry Coffin, de bron
Ik heb het gecompileerd met gcc 4.1.2, ik merk dat de uitvoer van het eerste programma alleen verbonden is met het sleutelwaardepaar dat ik invoeg (niets met de volgorde van optellen), bij invoegen 4 is de uitvoer 4, bij invoegen 6 paar , het resultaat is 6; Ik wil gewoon weten waarom dit is gebeurd?
toegevoegd de auteur Sprout_Wang, de bron

Container-wijziging maakt bestaande iterators ongeldig.

Het is gebruikelijk om iterator te krijgen voordat je hem gebruikt, en dan weg te gooien. U kunt voor als volgt gebruiken:

#include 
#include 
using namespace std;

int main ()
{
  map mymap;

  mymap[0] = 100;
  mymap[1] = 200;
  mymap[2] = 300;

 //show content:
  for (map::iterator it = mymap.begin(); it != mymap.end(); it++)
    cout << (*it).first << " => " << (*it).second << endl;

  return 0;
}
1
toegevoegd
Jerry, ik zou zo'n praktijk nog steeds niet aanbevelen. Het is aan de compiler om zich te conformeren aan de standaard.
toegevoegd de auteur lambdas, de bron
§23.1.2/8: "De invoegleden hebben geen invloed op de geldigheid van iterators en verwijzingen naar de container ..." (en operator [] is gedefinieerd in termen van invoegen ).
toegevoegd de auteur Jerry Coffin, de bron
Ik dacht dat het voor de hand lag, maar dat is een directe quote van de standaard.
toegevoegd de auteur Jerry Coffin, de bron

Kort antwoord: er is geen garantie dat de iterator nog geldig is na het wijzigen van de inhoud van de container.

Omdat de container hier een kaart is, meestal geïmplementeerd als een roodzwarte boom, wordt de structuur gewijzigd en opnieuw gebalanceerd tijdens invoegingen.

In het eerste voorbeeld initialiseert u de iterator pos naar het begin van de kaart. Op dit punt is de iterator geldig voor de huidige inhoud. Maar zodra u elementen begint toe te voegen, wijst de iterator niet langer naar de nieuwe positie begin van de gereorganiseerde container.

Dus de reden waarom het tweede voorbeeld werkt, is omdat u de iterator instelt op begin nadat alle wijzigingen aan de container zijn voltooid.

Over het algemeen is het een slecht idee om een ​​structuur aan te passen terwijl er overheen wordt nagedacht.

Deze vraag heeft wat meer details over de geldigheid van iterators:

1
toegevoegd
§23.1.2/8: "De invoegleden hebben geen invloed op de geldigheid van iterators en verwijzingen naar de container ..." (en operator [] is gedefinieerd in termen van invoegen ).
toegevoegd de auteur Jerry Coffin, de bron
Ik citeerde van de 03-standaard. En ja, u lijkt de overeenkomstige sectie in de C ++ 11-standaard te hebben gevonden.
toegevoegd de auteur Jerry Coffin, de bron
Downloaden van een kopie van de C ++ 11 conceptspecificatie nu ...
toegevoegd de auteur gavinb, de bron
@JerryCoffin Deze details proberen op te frissen en de nieuwste conceptspecificatie N3337 gebruiken (en een eerdere versie controleren) Ik zie deze details helemaal niet in sectie 23.1, wat slechts een kort overzicht is. Ik denk dat je 23.2.4/9 bedoelt, wat Associative Containers beschrijft?
toegevoegd de auteur gavinb, de bron

Iterators zijn bedoeld voor gebruik op een container die niet is gewijzigd sinds de iterator-instantiatie. De uitvoer van de code in het eerste voorbeeld is ongedefinieerd, volgens de c ++ -standaard (je zou nog steeds het gewenste resultaat kunnen krijgen, je bent er niet zeker van dat je het krijgt, en er is niet veel reden om het te verwachten).

In het eerste geval is er niets met de iterator gebeurd, maar de container waarnaar u wilt verwijzen, heeft wijzigingen ondergaan en bevindt zich niet noodzakelijkerwijs op dezelfde locatie in het geheugen.

1
toegevoegd
§23.1.2/8: "De invoegleden hebben geen invloed op de geldigheid van iterators en verwijzingen naar de container ..." (en operator [] is gedefinieerd in termen van invoegen ).
toegevoegd de auteur Jerry Coffin, de bron
Bedankt voor de correctie!
toegevoegd de auteur loki11, de bron