Як орієнтуватися через вектор за допомогою ітераторів? (C ++)


105

Мета - отримати доступ до "n-го" елемента вектора рядків замість оператора [] або методу "at". Як я розумію, ітератори можуть використовуватися для навігації по контейнерах, але я ніколи раніше не використовував ітераторів, і те, що я читаю, бентежить.

Якщо хтось міг би дати мені трохи інформації про те, як цього досягти, я би вдячний. Дякую.


Чи не вектори виключні для STL C ++? Я редагую його незалежно
кевін

Кевін: вектор - це загальний термін, який може бути використаний будь-якою мовою, зокрема, пов'язаними з математикою, такими як Mathematica або Matlab.
Гейб

@michael, так, ха-ха, я редагував це після коментаря Gabe.
Кевін

Відповіді:


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.

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-му елементі, в чому питання ОП. Крім того, будь-яка відповідь, що вимагає O (n) часової складності, наприклад ця, дуже погана. Доступ до n-го елемента вектора завжди повинен бути O (1).
Елліотт

@lashgar Я спробував це з масивом, але не вдалося. Це працює для масиву?
era s'q

@ eras'q, спробував з gcc 7.5.0на Ubuntu 18.04 і працює для масиву так само.
лашгар

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це дуже прикро використовувати (через використання параметрів поза), якщо ви знаєте, що маєте справу з ітераторами випадкового доступу. Рекомендував би лише в загальному коді, і якщо він не використовується багато (якщо алгоритм не підтримує ітераторів без випадкового доступу, так і бути - наприклад, std::sort міг би сортувати, std::listале це не так, оскільки це було б смішно неефективним ).
UncleBens

Звичайно, класичним прикладом може бути, якщо алгоритму насправді потрібен лише InputIterator, але з будь-якої причини він іноді пропускає вперед, тому ви хочете, щоб він був більш ефективним, якщо ітератор має випадковий доступ. Не варто обмежувати свій алгоритм випадковим доступом лише за допомогою operator+. Але питання явно стосувалося вектора, тому в першій частині вашої відповіді немає нічого поганого. Я просто подумав, що друга частина може означати, що "ти не можеш використовувати заздалегідь ітераторів випадкового доступу, навіть якщо хочеш" тому, хто ніколи не бачив advance.
Стів Джессоп

Добре, переробили цей біт і подали приклад з вектором.
дядькоБенс

другий рядок, Vectorмає бути нижній регістр
Lei Yang,

0

Ось приклад доступу до ithіндексу 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());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.