Итерация по вектору C++ с использованием цикла " for


Я новичок в языке C++. Я начал использовать векторы и заметил, что во всем коде, который я вижу, чтобы перебирать вектор через индексы, первый параметр for петли всегда что-то на основе вектора. В Java я мог бы сделать что-то вроде этого с ArrayList:

for(int i=0; i < vector.size(); i++){
   vector[i].doSomething();
}

почему я не вижу этого в C++? Это плохая практика?

7 98

7 ответов:

есть ли причина, по которой я не вижу этого в C++? Это плохая практика?

нет. Это не плохая практика, но она делает ваш код определенным гибкость.

обычно, до C++11 код для итерации по элементам контейнера использует итераторы, что-то вроде:

std::vector<int>::iterator it = vector.begin();

Это потому что это делает код более гибким.

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

Примечание: написание кода, который работает со всеми возможными стандартными библиотечными контейнерами, не так просто, как может показаться.

причина, по которой вы не видите такой практики, довольно субъективна и не может иметь определенного ответа, потому что я видел много кода, который использует ваш упомянутый способ, а не iterator код стиля.

могут быть причины людей, не считая vector.size() способ зацикливания:

  1. будучи параноиком о вызове size() каждый раз в цикле состояние. Однако либо это не проблема, либо это может быть тривиально исправлено
  2. предпочитая std::for_each() на for цикл
  3. позднее изменение контейнера с std::vector к другому (например map,list) также потребует изменения механизма зацикливания, потому что не каждый контейнер поддерживает size() стиль перекручивание

C++11 обеспечивает хорошее средство для перемещения через контейнеры. Это называется "range based for loop" (или "enhanced for loop" в Java).

С небольшим кодом вы можете пройти через полный (обязательно!)std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;

самый чистый способ итерации через вектор - через итераторы:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

или (эквивалент выше)

for (auto & element : vector) {
    element.doSomething ();
}

до C++0x необходимо заменить auto типом итератора и использовать функции-члены вместо глобальных функций begin и end.

это, вероятно, то, что вы видели. По сравнению с подходом, который вы упомянули, преимущество заключается в том, что вы не сильно зависите от типа vector. Если вы измените vector в другой класс "collection-type", ваш код, вероятно, все еще будет работать. Однако вы можете сделать что-то подобное и в Java. Концептуально разница невелика; C++, однако, использует шаблоны для реализации этого (по сравнению с дженериками в Java); следовательно, подход будет работать для всех типов, для которых begin и end функции определены, даже для неклассовых типов, таких как статические массивы. Смотрите здесь: как работает range-based для простых массивов?

правильный способ сделать это:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

где T-тип класса внутри вектора. Например, если класс был CActivity, просто напишите CActivity вместо T.

этот тип метода будет работать на каждом STL (не только векторы, что немного лучше).

Если вы все еще хотите использовать указатели, как это:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}

есть несколько веских причин использовать итераторы, некоторые из которых упоминаются здесь:

переключение контейнеров позже не делает недействительным ваш код.

т. е., если вы переходите от std::vector к std::list или std:: set, вы не можете использовать числовые индексы для получения вашего содержащегося значения. Использование итератора по-прежнему допустимо.

время выполнения ловли недопустимой итерации

Если вы измените свой контейнер в середине цикла, в следующий раз вы используйте свой итератор, он вызовет недопустимое исключение итератора.

С STL, программисты используют iterators для обхода контейнеров, так как итератор-это абстрактное понятие, реализованное во всех стандартных контейнерах. Например, std::list нет operator [] на всех.

Я был удивлен, что никто не упомянул, что итерация по массиву с целочисленным индексом позволяет вам легко писать ошибочный код, подписывая массив с неправильным индексом. Например, если у вас есть вложенные циклы с помощью i и j в качестве индексов, вы можете неправильно индексировать массив с j, а не i и таким образом ввести ошибки в программе.

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