2008-10-09 2 views
11

Обычно вы найдете STL код, как это:Является ли инициализация Итератора внутри для цикла рассмотренным плохим стилем и почему?

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter) 
{ 
} 

Но мы на самом деле есть рекомендации, чтобы написать это следующим образом:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); 
SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end(); 
for (; Iter != IterEnd; ++Iter) 
{ 
} 

Если вы беспокоитесь о обзорного, добавить обрамляющие фигурные скобки:

{ 
    SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(); 
    SomeClass::SomeContainer::iterator IterEnd = m_SomeMemberContainerVar.end(); 
    for (; Iter != IterEnd; ++Iter) 
    { 
    } 
} 

Предполагается, что это дает скорость и эффективность, особенно если вы программируете консоли, потому что функция .end() не вызывается на каждой итерации цикла. Я просто считаю, что повышение производительности является само собой разумеющимся, это звучит разумно, но я не знаю, сколько и это зависит от типа контейнера и фактической реализации STL. Но, используя этот стиль в течение пары месяцев, я действительно предпочитаю его в любом случае.

Причина, по которой читаемость: линия опрятная и аккуратная. С классификаторами и переменными-членами в реальном коде производства довольно просто действительно длинный для строк, если вы используете стиль в первом примере. Вот почему я преднамеренно заставил его иметь горизонтальную полосу прокрутки в этом примере, так что вы видите, о чем я говорю. ;)

С другой стороны, вы внезапно вводите переменные Iter во внешнюю область цикла for. Но тогда, по крайней мере, в среде, в которой я работаю, Iter был бы доступен во внешней области даже в первом примере.

Что вы думаете об этом? Есть ли профессионалы в первом стиле, кроме, возможно, ограничения объема Iter?

ответ

12

Если вы обернуть свой код в линии должным образом, встроенная форма будет одинаково читаема.Кроме того, вы всегда должны делать iterEnd = container.end() как оптимизация:

for (SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(), 
    IterEnd = m_SomeMemberContainerVar.end(); 
    Iter != IterEnd; 
    ++Iter) 
{ 
} 

Обновление: фиксированный код за советом paercebal в.

5

Первая форма (внутри цикла for) лучше, если итератор не нужен после цикла for. Он ограничивает область действия циклом for.

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

typedef SomeClass::SomeContainer::iterator MyIter; 

for (MyIter Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter) 
{ 
} 

Я рекомендовал бы короче имена ;-)

+0

не обязательно, в моей среде это законно и справедливо: «for (int i = 0; i steffenj 2008-10-09 20:27:12

+0

@Ferrucio: это недопустимо. C++. Возможно, вы используете более старую версию VC++, которая не соответствовала стандарту, но вам придется исправить это при обновлении – 2008-10-09 20:29:54

+0

. Правда, старые компиляторы могут по-прежнему принимать старый стандарт. В VC++ у вас есть возможность включить или выключить этот параметр. – Ferruccio 2008-10-09 20:30:27

0

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

0

Я нахожу второй вариант более удобочитаемым, так как вы не получаете одну гигантскую линию. Тем не менее, Ферруччио хорошо разбирается в сфере применения.

0

Я согласен с Ferruccio. Первый стиль может быть предпочтительным для некоторых, чтобы вывести вызов конца() из цикла.

Я мог бы также добавить, что C++ 0x будет на самом деле сделать обе версии гораздо чище:

for (auto iter = container.begin(); iter != container.end(); ++iter) 
{ 
    ... 
} 

auto iter = container.begin(); 
auto endIter = container.end(); 
for (; iter != endIter; ++iter) 
{ 
    ... 
} 
0

Я обычно пишу:

SomeClass::SomeContainer::iterator Iter = m_SomeMemberContainerVar.begin(), 
            IterEnd = m_SomeMemberContainerVar.end(); 

for(...) 
1

У меня нет особого мнения в том или ином виде, хотя время жизни итератора будет склонять меня к версии с расширенным охватом.

Однако читаемость может быть проблемой; которые могут помочь при использовании ЬурейеЕ поэтому тип итератора является немного более управляемым:

typedef SomeClass::SomeContainer::iterator sc_iter_t; 

for (sc_iter_t Iter = m_SomeMemberContainerVar.begin(); Iter != m_SomeMemberContainerVar.end(); ++Iter) 
{ 
} 

Не огромный шаг вперед, но немного.

1

У меня нет опыта в консоли, но в большинстве современных компиляторов C++ либо опция заканчивается как эквивалентная, за исключением вопроса о сфере видимости. Компилятор визуальной студии практически всегда даже в коде отладки помещает сравнение условий в неявную временную переменную (обычно это регистр). Таким образом, хотя логически это выглядит так, как вызов end() выполняется через каждую итерацию, оптимизированный скомпилированный код на самом деле только делает вызов один раз, и сравнение - это единственное, что выполняется каждое время субклиента через цикл.

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

9

Другой альтернативой является использование Еогеасп макроса, например boost foreach:

BOOST_FOREACH(ContainedType item, m_SomeMemberContainerVar) 
{ 
    mangle(item); 
} 

Я знаю, что макросы не рекомендуется в современном C++, но пока авто ключевое слово не будет доступен это лучший способ я нашел чтобы получить то, что является кратким и читаемым, и все еще полностью типичным и быстрым. Вы можете реализовать свой макрос, используя любой стиль инициализации, который обеспечивает лучшую производительность.

Также есть ссылка на связанную страницу о переопределении BOOST_FOREACH как foreach, чтобы избежать раздражающих всех шапок.

1

Это может быть сделано для несвязанного кода, но я также хотел бы вывести его на отдельную функцию и передать ему итераторы.

doStuff(coll.begin(), coll.end()) 

и есть ..

template<typename InIt> 
void doStuff(InIt first, InIt last) 
{ 
    for (InIt curr = first; curr!= last; ++curr) 
    { 
     // Do stuff 
    } 
} 

Чем нравится:

  • Никогда не говоря уже о уродливый тип итератора (или думать о том, является ли это константный или не-Const)
  • Если на каждой итерации есть выигрыш от не вызывающего конца(), я получаю его

Чем не нравится:

  • растягивает код
  • Overhead дополнительного вызова функции.

Но однажды у нас будет лямбда!

4

Нет, это плохая идея, чтобы удержать iter.end() до начала цикла. Если ваш цикл изменяет контейнер, конечный итератор может быть недействительным. Кроме того, гарантируется end() метод O (1).

Преждевременная оптимизация - это корень всего зла.

Кроме того, компилятор может быть умнее, чем вы думаете.

4

Посмотрев на это в г ++ при -O2 оптимизации (только для определенности)

Там нет никакой разницы в сгенерированном коде для станд :: вектор, станд :: список и станд :: карта (и друзья). С std :: deque крошечные накладные расходы.

В общем, с точки зрения производительности это мало чем отличается.

1

Я не думаю, что это плохой стиль. Просто используйте typedefs, чтобы избежать многословности STL и длинных строк.

typedef set<Apple> AppleSet; 
typedef AppleSet::iterator AppleIter; 
AppleSet apples; 

for (AppleIter it = apples.begin(); it != apples.end(); ++it) 
{ 
    ... 
} 

Spartan Programming - один из способов смягчить ваш стиль.

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