2013-06-14 13 views
5

Nie rozumiem dlaczego ten kod jest dokładnekopia algorytm back_inserter

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

coll.end() oznacza koniec wektora. Po tym, jak puszczam cokolwiek (co robi back_insert_iterator), co zwraca coll.end() jest tym samym, co było wcześniej lub czymś innym? Czy istnieje więcej niż jeden iterator kończący? Dlaczego metoda end() może być używana jako koniec kontenera, nawet po dodaniu nowej treści?

Co więcej, nie można zastosować kodu do kontenera listy - utknie. Jest to ważne, ponieważ w przypadku wektora push_back powoduje, że iteratory są niewiarygodne po ponownym przydzieleniu danych (gdy wywoływane są size()==capacity() i push_back()), podczas gdy w przypadku listy tak nie jest. Niż dlaczego kod wisi na liście?

Edycja: (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); 
} 

Odpowiedz

5

Wskaźniki/iteratory używane do określenia, które elementy mają być skopiowane, są oceniane raz, gdy wywoływana jest funkcja. Zasadniczo std::copy() zwiększy iterator kursora, który ostatecznie osiągnie end(), ponieważ vector<T>::const_iterator jest po prostu fantazyjnym wskaźnikiem T* na starej tablicy szkolnej.

Jak prawidłowo mentionned, jeśli push_back sprawia vector realokacji i przenieść dane gdzieś w pamięci, a następnie kolejnym elementem kopiowane będą miały niewłaściwy adres źródła, co jest prawdopodobne, aby skończyć z winy segmentaion.

Lista nigdy nie może zostać zakończona, ponieważ end() jest wskaźnikiem wartownika/strażnika, a można uzyskać tylko end(), inkrementując iterator na ostatnim elemencie listy. Tak więc adres end() sam się nie zmienia, ale ponieważ ciągle dodajesz element na końcu listy, nigdy nie dotrzesz do ostatniego elementu, więc std::copy() nigdy nie uzyska wskaźnika do end(). Coś jak pies goniący za ogonem.

Łatwiej zrozumieć dzięki ilustracjom i diagramom, odczytywaniu na podwójnie połączonej liście i węzłach wartowniczych, to wszystko będzie miało sens.

7

coll.end() nazywa przed rozpoczyna kopiowanie i tylna wstawka, więc zasadniczo kod jest taki sam jak

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

znaczenia, copy nie będzie ponownie -wartość coll.end(), więc nie zauważy/przeszkadza, że ​​wstawia do tego samego wektora, a to, co kiedyś było końcem wektora, nie jest końcem po pierwszych wstawkach.

Ten sam algorytm nie będzie kompilował kompilacji, ponieważ std::list nie ma metody . Jednak bez reserve powinien działać dla list s, ponieważ std::list::push_back nie unieważnia iteratorów. Masz rację, że std::vector::push_back unieważnia Iteratory po wystąpieniu realokacji, więc bardzo ważne jest wykonanie wywołania , ponieważ zapewnia, że ​​nie jest potrzebna realokacja.

+0

Wiem, że nie ma rezerwy() na liście. Próbowałem kodu dla listy. Program przechodzi w nieskończoną pętlę. –

+0

Czy możesz pokazać nam [SSCCE] (http:/sscce.org) kodu użytego do przetestowania tego na liście? –

+0

dodano sscce do pytania –