Итерация через вектор С++ с использованием цикла 'for'

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

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

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

Ответ 1

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

Нет. Это неплохая практика, но следующий подход придает вашему коду определенную гибкость.

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

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

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

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

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

Ответ 2

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

Ниже приводятся причины, по которым люди не рассматривают способ цикла vector.size():

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

С++ 11 обеспечивает хорошее средство для перемещения по контейнерам. Это называется "диапазон, основанный на цикле" (или "усиленный для цикла" в Java).

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

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

Ответ 3

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

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

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

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

До С++ 0x вам нужно заменить auto на тип итератора и использовать функции-члены, а не начинать и заканчивать глобальные функции.

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

Ответ 4

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

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();
}

Ответ 5

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

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

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

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

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

Ответ 6

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

Ответ 7

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

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