Самый простой способ сделать циклический итератор (циркулятор)?
У меня есть объект, который я хочу путешествовать в непрерывном цикле в игре. У меня есть ряд координат в std::vector
, который я хочу использовать в качестве путевых точек.
Есть ли способ сделать циклический std::vector<T>::iterator
(также известный как циркулятор)?
Лучшее, что я могу придумать, состоит в том, чтобы иметь два итератора, а затем всякий раз, когда первый итератор исчерпан, назначьте ему значение второго (которое не будет использоваться для чего-либо еще), но я даже не уверен, что он будет work - будет ли оператор присваивания копировать все, что использует итератор, чтобы удерживать индекс, или он будет просто ссылаться (и, следовательно, будет бесполезен после второго раунда)?
Я хочу, чтобы объект вел путевую точку навсегда (если только он не уничтожен, но этого не происходит в этом методе), но итератор будет вызываться только один раз для каждого кадра и должен возвращаться, чтобы я мог обновлять другие объекты в игре.
Решение должно работать с компилятором gcc и microsoft (если его невозможно записать в стандартном С++).
Ответы
Ответ 1
Хорошо, теперь ваша проблема понятна: -)
Взгляните на boost:: iterator_facade и boost:: iterator adapter. Они реализуют полный интерфейс итератора и ваш cycle_iterator
только для реализации нескольких методов, таких как increment(), декремент():
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() {
if(base_reference()==m_itEnd) {
base_reference()=m_itBegin;
} else {
++base_reference(); // increments the iterator we actually point at
}
}
// implement decrement() and advance() if necessary
};
Это, вероятно, не скомпилируется, но вы должны начать.
Изменить:
boost:: iterator_adaptor реализует полный интерфейс итератора с точки зрения нескольких функций. Он предоставляет стандартные реализации для increment()
, decrement()
, advance()
, distance_to()
, equal_to()
и dereference()
, используя базовый итератор, переданный в базовый класс iterator_adaptor
.
Если вам нужен только форвардный итератор, только один метод increment()
должен быть реализован, чтобы обернуть его, как только вы достигнете конечного итератора. Циклический итератор может быть двунаправленным, если вы реализуете decrement()
аналогичным образом. Если IteratorBase
сам является итератором с произвольным доступом, то итератор цикла также может быть произвольным доступом, а метод advance
и distance_to
должен быть реализован с использованием операций modulo.
Ответ 2
boost::iterator adaptor
- это путь, возьмите мое слово для этого;)
Говоря, я хочу указать на несколько подводных камней. Я не думаю, что могу отредактировать существующий ответ, так что несите меня.
Учитывая, что ваш базовый итератор будет представлять собой вектор, вам нужно быть осторожным, какие функции основного интерфейса вам необходимо реализовать. Если вы хотите, чтобы ваш cycle_iterator
был итератором с произвольным доступом, вам нужно все следующее:
increment()
decrement()
advance(n)
distance_to(j)
Теперь distance_to(j)
представляет собой несколько забавное понятие для cycle_iterator
, и его семантика может вызвать у вас неприятности. Этого можно избежать, ограничив категорию итератора адаптированного итератора либо передовым, либо двунаправленным. Вот так:
template <class BaseIterator>
class cycle_iterator
: public boost::iterator_adaptor<
cycle_iterator // Derived
, BaseIterator // Base
, boost::use_default // Value
, boost::forward_traversal_tag // CategoryOrTraversal
>
{ ... };
В этом случае вам нужно только выполнить приращение:
void increment()
{
if (++this->base_reference() == this->m_itEnd)
{
this->base_reference() = this->m_itBegin;
}
}
Для двунаправленного вам также потребуется декремент:
void decrement()
{
if (this->base_reference() == this->m_itBegin)
{
this->base_reference() = this->m_itEnd;
}
--this->base_reference();
}
Отказ от ответственности: я не запускал это через компилятор, поэтому я готов смущаться.
Ответ 3
Выполните собственную коллекцию из std::vector и предоставите собственную реализацию итератора, которая переопределяет операторы приращения и уменьшения.
В Интернете много обучающих программ. Например, посмотрите это сообщение в блоге