2008-10-09 11 views
34

Piszę kod między platformami między Windows i Mac.Jak wykonać iterację wstecz za pośrednictwem listy STL?

Jeśli list :: end() "zwraca iterator, który adresuje lokalizację po ostatnim elemencie na liście" i może być sprawdzany podczas przewijania listy do przodu, jaki jest najlepszy sposób przechodzenia wstecz?

Ten kod workson Mac, ale nie na systemie Windows (nie może zmniejszyć się poza pierwszym elemencie):

list<DVFGfxObj*>::iterator iter = m_Objs.end(); 
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ? 
{ 
} 

to działa w systemie Windows:

list<DVFGfxObj*>::iterator iter = m_Objs.end(); 
    do{ 
     iter--; 
    } while (*iter != *m_Objs.begin()); 

Czy istnieje inny sposób na przechodzenie do tyłu, że może być zaimplementowany w pętli for?

+1

Byłby to tylko przypadek wdrożenia, że ​​twój pierwszy przykład (iterator cykliczny, porównując z end()) będzie działać. – Justsalt

Odpowiedz

60

Użyj reverse_iterator zamiast iteratora. Użyj funkcji rbegin() & rend() zamiast begin() & end().

Inną możliwością, jeśli chcesz użyć makra BOOST_FOREACH, jest użycie makra BOOST_REVERSE_FOREACH wprowadzonego w kroku Boost 1.36.0.

+0

Dokumentacja dla iteratora i reverse_iteratora jest prawie taka sama. Iterator jest dwukierunkowy, więc jaka jest różnica? – AlanKley

+2

Różnica polega na tym, że nadal wykonujesz "++ Iter", aby zwiększyć iterator, w stosunku do "--Iter". Czy ja się mylę? – steffenj

+0

Nie, masz rację, co jest trochę dziwne, aby się cofnąć, ale ma również sens. Chociaż reverse_iterator wydaje się niepotrzebny, biorąc pod uwagę, że iterator jest dwukierunkowy. Dokumenty dla reverse_iterator mówią, że działa na odwróconej liście; z pewnością nie odwraca najpierw listy wewnątrz. – AlanKley

13

Prawdopodobnie potrzebujesz odwrotnych iteratorów. Z pamięci:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin(); 
for(; iter != m_Objs.rend(); ++iter) 
{ 
} 
+1

Dzięki, że brzmi dobrze. Ale wydaje się również marnować stworzenie specjalnego reverse_iteratora, gdy iterator ma być dwukierunkowy – AlanKley

+0

powinien to być "...> :: reverse_iterator iter = ..." – steffenj

+0

@AlanKley Myślę, że pętla for, którą umieściliście twoje pytanie jest w porządku. Powodem, dla którego myślę, że to działa, jest fakt, że funkcja członkowska .end() zwraca wartość wskaźnika, która jest przypisywana jako wartość następnego wskaźnika z ostatniego elementu, a także poprzedni wskaźnik na pierwszym elemencie. –

4

Jak już wspomniano przez Ferruccio, stosowanie reverse_iterator:

for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i) 
5

To powinno działać:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin(); 
for (; iter!= m_Objs.rend(); iter++) 
{ 
} 
16

najlepszy/Najprostszym sposobem, aby odwrócić iteracyjne lista jest (jak już wspomniano) do używania odwrotnych iteratorów rbegin/rend.

Jednakże, chciałbym wspomnieć, że implementowane są odwrócone iteratory przechowujące "bieżącą" pozycję iteratora jeden po drugim (przynajmniej w wersji GNU standardowej biblioteki).

Ma to na celu uproszczenie wdrażania, w celu zakresie w odwrotnej kolejności, aby mieć takie same semantykę jako zakres przodu [początek, koniec) i [rbegin, rozdzierać)

Oznacza to, że dereferencing iteracyjnej polega na tworzeniu nowej tymczasowe, a następnie zmniejszanie go każdym razem:

reference 
    operator*() const 
    { 
_Iterator __tmp = current; 
return *--__tmp; 
    } 

Zatem dereferencing reverse_iterator jest mniejsza niż normalny iteracyjnej.

Można jednak zamiast korzystać ze zwykłych iteratorów dwukierunkowych symulować odwrócić iteracja siebie, unikając tego narzutu:

for (iterator current = end() ; current != begin() ; /* Do nothing */) 
{ 
    --current; // Unfortunately, you now need this here 
    /* Do work */ 
    cout << *current << endl; 
} 

Testy wykazały to rozwiązanie będzie ~ 5 razy szybciej dla każdego dereference stosowanego w Ciało pętli.

Uwaga: Testowanie nie zostało wykonane przy użyciu powyższego kodu, ponieważ std :: cout stanowiłoby wąskie gardło.

Uwaga: różnica czasu "zegara ściennego" wynosiła ~ 5 sekund, a rozmiar std :: list wynosił 10 milionów elementów. Realistycznie, o ile rozmiar danych nie jest tak duży, po prostu trzymaj się rbegin() rend()!

+0

Patrząc na to jeszcze raz, prawdopodobnie chcesz zainicjować tylko przy bieżącym = --end(); i pozostaw krok inkrementacji wewnątrz pętli for. Chroniłoby to także przed pustą tablicą, której moja wersja powyżej nie jest. Zostawię oryginał na teraz, ponieważ nie testowałem. – mmocny

+0

Nie sądzę, aby to działało, chyba że zmienisz także stan pętli. W przeciwnym razie stracisz pierwszy element (bo pętla nie zostanie wykonana, jeśli 'current == begin()') – Griddo

+0

Pierwsza linia pętli for powoduje zmniejszenie, więc tak, tak myślę. Wystarczy napisać szybki test, aby spróbować! W każdym razie, nie uważam już, że jest to najmilszy sposób na użycie tego idiomu, a niektóre z ostatnio przeprowadzanych testów nie wykazują już znacznej poprawy prędkości w celu odwrócenia iteratorów przy pełnej optymalizacji. Warto jednak zwrócić uwagę na to, co dzieje się pod maską i odpowiednio przetestować! – mmocny

Powiązane problemy