Лучший способ перебрать контейнер - PullRequest
1 голос
/ 31 января 2009

Каковы преимущества / недостатки этих двух способов перебора контейнера / какой из них вы предпочитаете и почему:

for (MyClass::iterator i = m.begin(), e = m.end() ; i != e ; i++)
{
    // ...
}

или

for (MyClass::iterator i = m.begin() ; i != m.end() ; i++)
{
    // ...
}

Вспомогательный вопрос: i ++ или ++ i? Почему?

Ответы [ 7 ]

5 голосов
/ 31 января 2009

Если итератор нетривиален (т. Е. Не является указателем), ++ i определенно быстрее, поскольку не требует копирования во временное хранилище, которое может быть оптимизировано или не оптимизировано.

Первая форма немного быстрее, но может быть неправильной, если вы стираете или вставляете вещи в цикл.

Для простой итерации над контейнером я использую

#define foreach BOOST_FOREACH // in some header

foreach(MyType &element, any_container) {
  // deal with element
}

большую часть времени для краткости и ясности.

3 голосов
/ 31 января 2009

Если у вас не отключены оптимизации, оба варианта эквивалентны. Что касается i ++ или ++ i, ++ i более эффективен, поскольку не требует временного значения.

2 голосов
/ 31 января 2009

Для обычных итераторов stl нет большой разницы, однако, если ваши коллекции сложны, и запрос на завершение обходится дорого, тогда запрос только на один раз может быть быстрее.

Точно так же для ++ я против i ++, ++ я могу быть более дорогой операцией, когда итератор представляет собой сложный класс (а не просто указатель, как в итераторах stl) с i ++, что происходит в том, что он увеличивает итератор, но возвращение копии итератора в его предыдущем состоянии. для ++ i он возвращает итератор в его текущем состоянии, поэтому может просто вернуть ссылку на себя.

Обычно лучше оптимизировать ваш код только тогда, когда ваш профилировщик обнаружит, что там есть проблема - лучше сделать код как можно более читабельным.

1 голос
/ 31 января 2009

Первый быстрее, потому что end() не вызывается на каждой итерации. И нет, оптимизатор не может легко кешировать это для вас, потому что он не знает, изменился ли размер контейнера в этой итерации (и, следовательно, конец перемещен). Это также относится к const-контейнеру из-за проблемы с алиасами.

i++ возвращает копию i, затем увеличивает. ++i увеличивается, затем возвращает увеличенное значение. Следовательно, когда вы отбрасываете возвращаемое значение, используйте ++i, потому что оно должно выполнять меньше работы (без копирования). Оптимизатор, скорее всего, исправит встроенный вызов i++, так что он так же быстр, как ++i, но не полагайтесь на это.

Me? Я использую

for(int i = 0; i < m.size(); i++) {
    // ... do something with m[i]
}

Потому что это самый короткий и самый понятный. Почему int, а не MyClass::size_type? Потому что это проще, и мне никогда не приходилось беспокоиться о крайних случаях. Почему i++? Потому что для базовых типов он всегда оптимизирован до ++i, и это менее запутанно для коллег. В качестве бонуса я также могу использовать i в качестве числового значения. С итератором мне пришлось бы вести отдельный счетчик или использовать std::distance.

obecalp указывает, что это не работает с половиной стандартных контейнеров, таких как list и map. На самом деле те требуют использования правильного итератора. Кроме того, вы всегда должны использовать iterator при написании универсального кода.

1 голос
/ 31 января 2009

Вообще-то я всегда делаю второе, хотя иногда я волнуюсь, если несколько звонков, чтобы закончить, вообще все замедляют. У меня сложилось впечатление, что это будет оптимизировано, но я точно не знаю.

И ++ я определенно. Он никогда не медленнее, чем я ++, во всяком случае, быстрее.

0 голосов
/ 19 ноября 2016

Цикл C ++ «для каждого элемента в контейнере» наиболее эффективен, когда контекст не требует итеративной логики.

for(Item i : Container)
{
     dosomething(i);
}
0 голосов
/ 31 января 2009

Boost.Foreach представляет хороший способ:

#define foreach         BOOST_FOREACH
// ...
Container<Item> container;
// ...
foreach (Item item, container) {
  // do some stuff with the item
}
...