Як орієнтуватися через вектор за допомогою ітераторів? (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.