2009-11-23 15 views
13

Mam obiekt, który chcę podróżować w pętli ciągłej w grze. Mam serię współrzędnych w std::vector, które chcę użyć jako punktów nawigacyjnych.Najprostszy sposób na cykliczny iterator (cyrkulator)?

Czy istnieje sposób na wykonanie cyklicznego std::vector<T>::iterator (zwanego również cyrkulatorem)?

Najlepsze, co mogę wymyślić, to mieć dwa iteratory, a potem, gdy pierwszy iterator zostanie wyczerpany, przypisz mu wartość drugiego (który nie byłby użyty do zrobienia czegokolwiek innego), ale nie jestem nawet pewien będzie działać - czy operator przydziału skopiuje to, czego używa iterator do przechowywania indeksu, czy też będzie się on jedynie odnosił (a zatem będzie bezużyteczny po drugiej rundzie)?

Chcę, aby obiekt przemieścił punkt orientacyjny na zawsze (chyba że zostanie zniszczony, ale nie nastąpi to w tej metodzie), ale iterator będzie wywoływany tylko raz dla każdej klatki i musi zostać zwrócony, aby można było zaktualizować drugi obiekty w grze.

Rozwiązanie musi działać na kompilatorze gcc i microsoft (jeśli nie można go zapisać w standardowym C++).

+0

Napisałem taki iterator, więc jest to zdecydowanie możliwe =) Jedyne, co pamiętam, to to, że operator porównania

+0

Z drugiej strony naprawdę potrzebujesz operatora ''? To trochę psotne, aby ukryć cykliczne zachowanie! –

Odpowiedz

21

Ok, teraz problem jest wyraźniejszy :-)

Spójrz na boost :: iterator_facade i boost :: iterator adaptera. Realizują pełny interfejs iterator i twoi cycle_iterator tylko jak zaimplementować kilka metod, takich jak przyrost(), zmniejszania():

template<class IteratorBase> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
      cycle_iterator,  // the derived class overriding iterator behavior 
      IteratorBase,  // the base class providing default behavior 
      boost::use_default, // iterator value type, will be IteratorBase::value_type 
      std::forward_iterator_tag, // iterator category 
      boost::use_default // iterator reference type 
     > 
{ 
    private: 
    IteratorBase m_itBegin; 
    IteratorBase m_itEnd; 

    public: 
    cycle_iterator(IteratorBase itBegin, IteratorBase itEnd) 
     : iterator_adaptor_(itBegin), m_itBegin(itBegin), m_itEnd(itEnd) 
    {} 

    void increment() { 
     /* Increment the base reference pointer. */ 
     ++base_reference(); 

     /* Check if past-the-end element is reached and bring back the base reference to the beginning. */ 
     if(base_reference() == m_itEnd) 
      base_reference() = m_itBegin; 
    } 

    // implement decrement() and advance() if necessary 
    }; 

To prawdopodobnie nie kompiluje ale powinno Ci zacząć.

Edit:

boost::iterator_adaptor realizuje pełny interfejs iterator pod względem kilku funkcji. Udostępnia domyślne implementacje dla increment(), decrement(),, EASY, E, C

Jeśli wszystko czego potrzebujesz, to iterator do przodu, tylko metoda increment() musi zostać zaimplementowana, aby zawinąć po osiągnięciu iteratora końcowego. Cykliczny iterator może być dwukierunkowy, jeśli implementujesz decrement() w podobny sposób. Jeśli IteratorBase sam jest iteratorem dostępu swobodnego, iterator cyklu może również być dostępem swobodnym, a metoda advance imusi być zaimplementowana za pomocą operacji modulo.

+0

+1: 'iterator_adaptor' jest najprostszym sposobem na pisanie iteratorów, a przykłady są po prostu świetne (w szczególności, jak napisać tylko jeden adapter i uzyskać zarówno wersje const, jak i non-const). –

+0

Czy istnieje sposób na zrobienie binarnych operatorów takich jak '! =' do pracy z argumentami typu 'cycle_iterator ' i 'IteratorBase'? Próbuję porównać iterator "cycle_iterator" i "zwykły" i trzeba w tym przypadku jawnie przeciążyć. – Mikhail

+1

'increment()' potrzebuje metincji pracy - Myślę, że idealnie, że inkrementacja wartości przed 'end' powinna dać ci' begin'ning, a nie 'end' w cyklicznym iteratorze. – Yakk

-5

Wykreuj własną kolekcję ze std :: vector i podaj własną implementację iteratora, która zastępuje operacje dekrementacji przyrostowej &.

Istnieje wiele samouczków w Internecie. Na przykład, spójrz na this blog post

+3

pochodzą z wektora? Czy kiedykolwiek to robiłeś? Nie zgadzam się. Zamiast agregować! – xtofl

+0

NIGDY nie widziałem klasy wywodzącej się ze standardowego kontenera, który był dla niej lepszy. Agregat. –

+1

Zgadzam się z komentarzami. Odpowiedź wysłana w pośpiechu, żałując w czasie wolnym. Nie można go jednak usunąć, ponieważ została przyjęta jako odpowiedź. –

7

boost::iterator adaptor jest droga, wziąć za to moje słowo;)

Mając na uwadze powyższe chciałbym zwrócić uwagę na kilka pułapek. Nie wydaje mi się, żebym mógł edytować istniejącą odpowiedź, więc proszę o mnie.

Biorąc pod uwagę, że twój iterator bazowy będzie wektorem, musisz uważać, które funkcje interfejsu rdzeniowego musisz wdrożyć.Jeśli chcesz, aby cycle_iterator być iterator o swobodnym dostępie trzeba wszystkie z poniższych kryteriów:

increment() 
decrement() 
advance(n) 
distance_to(j) 

Teraz distance_to(j) jest nieco zabawny pomysł na cycle_iterator i jego semantyka może Ci w różnego rodzaju kłopoty. Można tego uniknąć, ograniczając kategorię iteratora dostosowanego iteratora do przodu lub do dwukierunkowości. Tak:

template <class BaseIterator> 
class cycle_iterator 
    : public boost::iterator_adaptor< 
     cycle_iterator     // Derived 
     , BaseIterator     // Base 
     , boost::use_default    // Value 
     , boost::forward_traversal_tag // CategoryOrTraversal 
    > 
{ ... }; 

w tym przypadku tylko trzeba realizować przyrost:

void increment() 
{ 
    if (++this->base_reference() == this->m_itEnd) 
    { 
    this->base_reference() = this->m_itBegin; 
    } 
} 

Przez dwukierunkowy trzeba również ubytek:

void decrement() 
{ 
    if (this->base_reference() == this->m_itBegin) 
    { 
    this->base_reference() = this->m_itEnd; 
    } 
    --this->base_reference(); 
} 

Disclaimer: nie uruchomić to przez kompilator, więc jestem gotów się zawstydzić.

+0

Myślę, że ogólnie 'distance_to (j)' może zwracać albo dodatnią, albo ujemną liczbę, która po dodaniu do iteratora, daje iterator wskazujący na 'j'. Możesz zdecydować, że chcesz zwrócić najmniejszą taką liczbę, która jest nieujemna, lub taką, która ma najmniejszą wartość bezwzględną. W większości przypadków ten ostatni prawdopodobnie ma największy sens - lub czy istnieje mocny argument przeciwny (kontrprzykład)? –

Powiązane problemy