2010-06-12 3 views
2

Я работаю на Aplication, где я рисую несколько изображений, как это:Как получить число циклов при использовании итератора в C++?

void TimeSlice::draw(float fX, float fY) { 
list<TimeSliceLevel*>::iterator it = levels.begin(); 
float level_x = x; 
float level_y = y; 
while(it != levels.end()) { 
    (*it)->draw(level_x,level_y); 
    level_y += (*it)->height; 
    ++it; 
} 

}

Хотя это немного неправильно. Мне нужно расположить TimeSliceLevel * на X. Когда у меня есть , я получаю цикл for(int i = 0; i < slices.size(); ++i), я могу использовать x = i * width. Хотя я использую итератор, поскольку мне много раз говорили, что это хорошее программирование:> и мне интересно, имеет ли итератор число «индекс», которое я могу использовать для вычисления новой позиции X? (Так что это еще вопрос об использовании итераторов)

С наилучшими пожеланиями, Поллукс

+1

Это действительно неэффективно для всех случаев, отличных от std :: vector или массивов. В любом случае индекс можно рассчитать как: std :: size_t index = std :: distance (levels.begin(), it); – Hippicoder

ответ

3

Нет, это не так. Если вам нужен целочисленный индекс, используйте for-loop. Несмотря на то, что некоторые итераторы-экстремисты хотели бы, чтобы вы верили, for-loops все еще имеют свое место в коде на C++.

+3

По списку? Разве это не сделало бы O (N^2)? – 2010-06-12 18:41:39

+2

@Paul Я почти никогда не пользуюсь списками, и я не заметил, что OP это делал. Если ему действительно нужен список, то итератором является ответ, но мой опыт в том, что большинство людей выбирают список без уважительной причины и почти всегда будут лучше обслуживаться с помощью вектора или deque. – 2010-06-12 18:47:18

+0

Он хочет как целочисленный индекс (для смещения X), так и итератор (для доступа к списку). Я бы не стал беспокоиться, но все равно использовать цикл for: 'for (iterator i = collection.begin(); i! = Collection.end(); ++ i, x + = width) {/ * draw (x, y) * /} ' – MSalters

7

Это не так, поскольку итераторы могут использоваться для других целей, кроме того, что цикл начинается от начала до конца упорядоченного индексированного списка. Вам нужно следить за индексом отдельно и увеличивать его каждый проход:

list<TimeSliceLevel*>::iterator it; 
int index; 

for(it = levels.begin(), index = 0; it != levels.end(); ++it, ++index) { 
    ... 
} 
0

Вы должны написать что-то вроде

size_t index = 0; 
for (list<...>::const_iterator it = y.begin(); it != y.end(); ++it) { 
    // Do your actions based on `index` 
    ++index; 
} 

и, ну, это иногда подходит.

С другой стороны, вы можете реорганизовывать (перепланировка) приложение таким образом, что ваш фактический цикл рисования не должен делать все эти x += something, y += something2, ..., а действовать следующим образом:

foreach (Level* level, list) { 
    level->draw(backend); 
} 

It иногда может быть сложным, но, на мой взгляд, этот подход может сэкономить вам много времени, если ваше приложение вырастет до чего-то «большой».

+0

Спасибо Kotti, как бы я вычислил позицию тогда? и что такое бэкэнд? – pollux

+0

@pollux Это сложный вопрос, и он связан с вашей проблемной областью. Моя основная идея заключалась в том, что вы могли хранить ваши 'x' и' y' в своих кусках уровня (или что-то еще) или сделать их производными от какого-то основного суперкласса Object. * К сожалению, я не могу вам много советовать, потому что я фактически ничего не знаю о том, как работает ваше приложение *. –

+0

@pollux 'Backend' представляет собой абстрактный абстрактный движок/интерфейс рендеринга. Например, вы могли бы иметь * WinAPI-бэкенд, бэкэнд DirectX, бэкэнд OpenGL *, и вы должны передать его методу рисования объекта, чтобы ваш объект знал ** «где рисовать». ** –

0

ВЫ МОЖЕТЕ НО ТОЛЬКО для итератора с произвольным доступом. Если это итератор с произвольным доступом, вы можете вычесть свой итератор из начального итератора для получения индекса (без сохранения отдельной индексной переменной int).

for (vector<int>::const_iterator cit = v.begin(); cit != v.end(); ++cit) 
{ 
    cout << "This is element no: " << cit - v.begin() << endl; 
} 

В вашем примере, к сожалению, вы не сможете это сделать, потому что вы используете зЬй :: список, который только двунаправленный итератор. Используйте std :: vector, и вы можете сделать это как мой пример.

+0

Кстати, я должен упомянуть о том, что я «Я действительно не сторонник этого, как способ пойти. Я не вижу ничего плохого цикла с индексной переменной, если вам нужен индекс. Я просто указываю, что это действительно так. – ryaner

1

Для некоторых типов итераторов, просто вычесть текущий итератор из исходного итератора:

index = it - levels.begin()

Поскольку это не работает для станда :: списка итераторов, просто отслеживать индекс явно с переменным, как упомянутых выше. Преимущество использования итератора и контейнера не теряется. Вы добавляете требование, которое контейнер не предоставляет.

2

Из итератора можно перейти от индекса.Есть по крайней мере два способа:

  1. Использование - для случайных итераторы доступа (т.е. i - container.begin())
  2. Использование std::distance (т.е. std::distance(containter.begin(), i)). Это более «общее» решение и будет выполнять одинаково в случайном итератора доступ случая к - благодаря специализации, но будет иметь ужасные последствия производительности иначе

Однако, я бы не рекомендовал либо из них, как это обфускации кода (и может быть неэффективным). Вместо этого, как говорили другие, используйте дополнительный счетчик. Нет ничего «неправильного» с использованием индексов, когда это необходимо, а предпочтительные итераторы должны быть ориентиром для написания «общего» кода, так как тогда вы можете применить алгоритм к другому контейнеру или подмножеству контейнера, и т. д.

+0

+1 для std :: distance, а также для указания, что это вообще не нужно. В случае OP просто увеличение индекса с каждой итерацией было бы неплохо. – stinky472

Смежные вопросы