Как перемещаться по вектору с помощью итераторов? (C ++)

105

Цель состоит в том, чтобы получить доступ к «n-му» элементу вектора строк вместо оператора [] или метода «at». Насколько я понимаю, итераторы можно использовать для навигации по контейнерам, но я никогда раньше не использовал итераторы, и то, что я читаю, сбивает с толку.

Если бы кто-нибудь мог дать мне информацию о том, как этого добиться, я был бы признателен. Спасибо.

Кевин
источник
Разве векторы не являются эксклюзивными для STL C ++? Я отредактирую это в любом случае
Кевин
kevin: вектор - это общий термин, который может использоваться в любом языке, особенно в математических, таких как Mathematica или Matlab.
Гейб
@michael, да, ха-ха, я отредактировал это после комментария Гейба.
Кевин

Ответы:

112

Вам нужно сделать использование beginи endметоды в vectorклассе, которые возвращают итератор со ссылкой на первый и последний элемент соответственно.

using namespace std;  

vector<string> myvector;  // a vector of stings.


// push some strings in the vector.
myvector.push_back("a");
myvector.push_back("b");
myvector.push_back("c");
myvector.push_back("d");


vector<string>::iterator it;  // declare an iterator to a vector of strings
int n = 3;  // nth element to be found.
int i = 0;  // counter.

// now start at from the beginning
// and keep iterating over the element till you find
// nth element...or reach the end of vector.
for(it = myvector.begin(); it != myvector.end(); it++,i++ )    {
    // found nth element..print and break.
    if(i == n) {
        cout<< *it << endl;  // prints d.
        break;
    }
}

// other easier ways of doing the same.
// using operator[]
cout<<myvector[n]<<endl;  // prints d.

// using the at method
cout << myvector.at(n) << endl;  // prints d.
codaddict
источник
5
Это упускает из виду тот факт, что std::vectorесть итераторы произвольного доступа.
sbi
24
Независимо от того, знаете ли вы, что тип итератора является произвольным или нет, «лучший» способ переместить итератор на n пробелов - это не писать собственный цикл, а вызывать std::advance(it, n). Он определен так, чтобы делать именно то, что вы хотите, и он будет автоматически использовать, it + nесли итератор помечен как произвольный доступ, или выполнить цикл, если это необходимо.
Стив Джессоп,
61

Обычно итераторы используются для линейного доступа к элементам контейнера; однако с помощью «итераторов произвольного доступа» можно получить доступ к любому элементу таким же образом, как и operator[].

Для доступа к произвольным элементам вектора vec вы можете использовать следующее:

vec.begin()                  // 1st
vec.begin()+1                // 2nd
// ...
vec.begin()+(i-1)            // ith
// ...
vec.begin()+(vec.size()-1)   // last

Ниже приведен пример типичного шаблона доступа (более ранние версии C ++):

int sum = 0;
using Iter = std::vector<int>::const_iterator;
for (Iter it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

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

sum = 0;
for (Iter it = lst.begin(); it!=lst.end(); ++it) {
    sum += *it;
}

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

Другой вариант использования std::for_eachи лямбды:

sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int i) { sum += i; });

Начиная с C ++ 11 вы можете использовать, autoчтобы не указывать очень длинное и сложное имя типа итератора, как было показано ранее (или даже более сложное):

sum = 0;
for (auto it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

И, кроме того, для каждого варианта есть более простой вариант:

sum = 0;
for (auto value : vec) {
    sum += value;
}

И, наконец, std::accumulateнужно быть осторожным при добавлении целых чисел или чисел с плавающей запятой.

Михаил Аарон Сафян
источник
53

В C ++ - 11 вы можете:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v)
{
   // access by value, the type of i is int
   std::cout << i << ' ';
}
std::cout << '\n';

Смотрите здесь варианты: https://en.cppreference.com/w/cpp/language/range-for

лашгар
источник
4
ПОЧЕМУ ЭТО НУЛЬ ЛАЙКОВ ?! <3
jperl
3
@jperl Опубликовано с опозданием на 8 лет. Потребуются следующие 8 лет, чтобы набрать достаточно голосов :)
lashgar
@jperl, ну ответ не по теме. Хотя эта функция цикла хороша, она не помогает вам узнать, когда вы находитесь в n-м элементе, а это вопрос OP. Кроме того, любой ответ, требующий O (n) временной сложности, такой как этот, очень плох. Доступ к n-му элементу вектора всегда должен быть O (1).
Эллиотт
@lashgar Я пробовал это с массивом, но не смог. Это работает для массива?
era s'q
@ eras'q, пробовал с gcc 7.5.0на Ubuntu 18.04 и работает с массивом точно так же.
lashgar
17

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

Вы можете получить доступ к n-му элементу, добавив n к итератору, возвращаемому из begin()метода контейнера , или вы можете использовать оператор [].

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

int sixth = *(it + 5);
int third = *(2 + it);
int second = it[1];

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

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

std::advance(it, 5);
int sixth = *it;
Дядя Бен
источник
1
Вы также можете использовать advanceитераторы с произвольным доступом или итераторы неизвестной категории, поскольку в этом случае гарантируется, что он будет работать в постоянное время. Вот почему пользовательские итераторы должны быть правильно помечены.
Стив Джессоп,
Действительно, но advanceэто действительно раздражает (из-за использования параметра out), если вы знаете, что имеете дело с итераторами произвольного доступа. Рекомендовал бы только в общем коде, и если бы он не использовался часто (если алгоритм не поддерживает итераторы без произвольного доступа, пусть так и будет - например, std::sort может сортировать, std::listно это не так, потому что это было бы смехотворно неэффективным ).
UncleBens 07
Конечно, классический пример - если вашему алгоритму действительно нужен только InputIterator, но по какой-то причине он иногда пропускает вперед, поэтому вы хотите, чтобы он был более эффективным, если итератор имеет произвольный доступ. Не стоит ограничивать свой алгоритм произвольным доступом только с помощью operator+. Но вопрос был явно о векторе, поэтому в первой части вашего ответа нет ничего плохого. Я просто подумал, что вторая часть может подразумевать «вы не можете использовать advance с итераторами произвольного доступа, даже если хотите» для кого-то, кто никогда advanceраньше не видел .
Стив Джессоп,
Хорошо, переформулировал этот бит и дал пример с вектором.
UncleBens 07
вторая строка Vectorдолжна быть в нижнем регистре
Лей Ян
0

Вот пример доступа к ithиндексу a std::vectorс использованием std::iteratorвнутри цикла, который не требует увеличения двух итераторов.

std::vector<std::string> strs = {"sigma" "alpha", "beta", "rho", "nova"};
int nth = 2;
std::vector<std::string>::iterator it;
for(it = strs.begin(); it != strs.end(); it++) {
    int ith = it - strs.begin();
    if(ith == nth) {
        printf("Iterator within  a for-loop: strs[%d] = %s\n", ith, (*it).c_str());
    }
}

Без цикла

it = strs.begin() + nth;
printf("Iterator without a for-loop: strs[%d] = %s\n", nth, (*it).c_str());

и используя atметод:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
хмофрад
источник