2013-06-14 12 views
5

Ich verstehe nicht, warum dieser CodeKopie Algorithmus mit back_inserter

vector<int> coll; 
coll.reserve(2*coll.size()); 
copy (
    coll.begin(), coll.end(), // zrodlo 
    back_inserter(coll)   // przeznaczenie 
); 

coll.end() stellt das Ende des Vektors genau ist. Nachdem ich irgendwas gepush_back (wie back_insert_iterator tut) was coll.end() zurückgibt ist das gleiche, was vorher war oder etwas anderes? Gibt es mehr als einen abschließenden Iterator? Warum kann end() als Containerende verwendet werden, auch wenn neuer Inhalt hinzugefügt wird?

Darüber hinaus können Sie den Code nicht auf Listencontainer anwenden - es bleibt hängen. Das ist wichtig, weil im Falle des Vektors push_back Iteratoren nach der Neuzuordnung von Daten unzuverlässig macht (wenn size()==capacity() und push_back() genannt werden), während dies im Falle einer Liste nicht der Fall ist. Dann warum hängt der Code für die Liste?

Edit: (sscce)

#include <iostream> 
#include <list> 
#include <algorithm> 
using namespace std; 

template <class T> 
inline void PRINT_ELEMENTS (const T& coll, const char* optcstr="") 
{ 
    typename T::const_iterator pos; 

    std::cout << optcstr; 
    for (pos=coll.begin(); pos!=coll.end(); ++pos) { 
     std::cout << *pos << ' '; 
    } 
    std::cout << std::endl; 
} 

int main(){ 
    list<int> coll; 

    list<int>::iterator end = coll.end(); 
    copy (
    coll.begin(), coll.end(), // zrodlo 
    back_inserter(coll)   // przeznaczenie 
    ); 
    cout << *end << endl; 
    PRINT_ELEMENTS(coll); 
} 

Antwort

5

Die begin() und end() Zeiger/Iteratoren zum Bestimmen, was kopiert werden soll, werden einmal beim Aufruf der Funktion ausgewertet. Also im Wesentlichen std::copy() inkrementieren wird es Cursor Iterator, der schließlich end() erreichen wird, weil vector<T>::const_iterator ist nur eine Phantasie T* Zeiger auf eine Old-School-Array. Wenn Sie push_back die vector neu zuordnen und die Daten an anderer Stelle im Speicher verschieben, hat das nächste kopierte Element eine falsche Adresse für die Quelle, was wahrscheinlich zu einem Segmentationsfehler führt.

Für eine Liste kann sie niemals enden, weil end() ein Sentinel-/Guard-Pointer ist und Sie nur end() erreichen können, indem Sie den Iterator für das letzte Element der Liste inkrementieren. Die Adresse end() selbst ändert sich nie, aber da Sie ständig ein Element am Ende der Liste hinzufügen, erreichen Sie niemals das letzte Element, so dass std::copy() niemals einen Zeiger auf end() erhalten kann. Wie ein Hund, der seinem Schwanz hinterher jagt.

Es ist einfacher zu verstehen mit Abbildungen und Diagrammen, lesen Sie auf doppelt verknüpften Liste und Sentinel-Knoten, es wird alles Sinn machen.

7

coll.end()vor das Kopieren und wieder Einsetzen beginnt genannt wird, so im Wesentlichen der Code ist der gleiche wie

coll.reserve(2*coll.size()); 
auto oldEnd = coll.end(); 
copy (coll.begin(), oldEnd, back_inserter(coll)); 

Bedeutung hat, wird copy nicht wieder -bewerte coll.end(), so wird es nicht merken, dass es in denselben Vektor eingefügt wird, und dass das, was einmal das Ende des Vektors war, nicht das Ende nach den ersten Einfügungen ist.

Derselbe Algorithmus wird nicht kompilieren für Listen, weil std::list keine reserve Methode hat. Ohne reserve sollte es jedoch für list s funktionieren, da std::list::push_back Iteratoren nicht ungültig macht. Sie haben Recht, dass std::vector::push_back Iteratoren bei der Neuzuweisung ungültig macht. Daher ist es sehr wichtig, den Aufruf reserve auszuführen, da sichergestellt wird, dass keine Neuzuweisung erforderlich ist.

+0

Ich weiß, dass es keine Reserve() in der Liste gibt. Ich habe den Code für die Liste ausprobiert. Das Programm gerät in eine Endlosschleife. –

+0

Können Sie uns eine [SSCCE] (http:/sscce.org) des Codes, den Sie verwendet haben, um das auf der Liste zu testen? –

+0

sscce zu Frage hinzugefügt –