Jak poruszać się po wektorze za pomocą iteratorów? (C ++)

105

Celem jest uzyskanie dostępu do „n-tego” elementu wektora łańcuchów zamiast operatora [] lub metody „at”. Z tego, co rozumiem, iteratory mogą być używane do poruszania się po kontenerach, ale nigdy wcześniej nie korzystałem z iteratorów, a to, co czytam, jest mylące.

Byłbym wdzięczny, gdyby ktoś mógł mi udzielić informacji, jak to osiągnąć. Dziękuję Ci.

kevin
źródło
Czy wektory nie są wyłączne dla STL języka C ++? Zmienię to niezależnie
kevin
kevin: vector to termin ogólny, który może być używany w dowolnym języku, szczególnie w językach matematycznych, takich jak Mathematica lub Matlab.
Gabe
@michael, yeah haha, zredagowałem to po komentarzu Gabe.
kevin

Odpowiedzi:

112

Musisz skorzystać z metody begini klasy, która zwraca iterator odnoszący się odpowiednio do pierwszego i ostatniego elementu.endvector

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
źródło
5
To pomija fakt, że std::vectorma iteratory dostępu swobodnego.
sbi
24
Niezależnie od tego, czy wiesz, że typ iteratora to dostęp losowy, czy nie, „najlepszym” sposobem na przesunięcie iteratora do przodu o n spacji nie jest napisanie własnej pętli, ale wywołanie std::advance(it, n). Jest zdefiniowany, aby robić dokładnie to, co chcesz, i automatycznie użyje, it + njeśli iterator jest oznaczony jako losowy dostęp lub zrobi pętlę, jeśli będzie to konieczne.
Steve Jessop
61

Zazwyczaj iteratory są używane do uzyskiwania dostępu do elementów kontenera w sposób liniowy; jednakże dzięki „iteratorom dostępu swobodnego” możliwy jest dostęp do dowolnego elementu w taki sam sposób, jakoperator[] .

Aby uzyskać dostęp do dowolnych elementów w wektorze vec , możesz użyć:

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

Poniżej znajduje się przykład typowego wzorca dostępu (wcześniejsze wersje C ++):

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

Zaletą korzystania z iteratora jest to, że ten sam wzorzec można zastosować w innych kontenerach :

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

Z tego powodu bardzo łatwo jest stworzyć kod szablonu, który będzie działał tak samo niezależnie od typu kontenera . Kolejną zaletą iteratorów jest to, że nie zakładają one, że dane są rezydentne w pamięci; na przykład można stworzyć iterator do przodu, który może czytać dane ze strumienia wejściowego lub po prostu generuje dane w locie (np. generator zakresu lub liczb losowych).

Inna opcja przy użyciu std::for_eachi lambd:

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

Od C ++ 11 możesz użyć, autoaby uniknąć określania bardzo długiej, skomplikowanej nazwy typu iteratora, jak widać wcześniej (lub nawet bardziej złożonej):

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

Ponadto istnieje prostszy wariant dla każdego:

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

I wreszcie jest miejsce, w std::accumulatektórym musisz uważać, czy dodajesz liczby całkowite, czy zmiennoprzecinkowe.

Michael Aaron Safyan
źródło
53

W C ++ - 11 możesz:

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';

Zobacz tutaj warianty: https://en.cppreference.com/w/cpp/language/range-for

lashgar
źródło
4
DLACZEGO TO ZERO PODOBA ?! <3
jperl
3
@jperl Opublikowano 8 lat później. Zdobycie wystarczającej liczby pozytywnych głosów zajmie kolejne 8 lat :)
lashgar
@jperl, odpowiedź jest nie na temat. Chociaż ta funkcja pętli jest fajna, nie pomaga ci wiedzieć, kiedy jesteś w n-tym elemencie, co jest pytaniem OP. Ponadto każda odpowiedź, która wymaga złożoności O (n) czasowej, taka jak ta, jest bardzo zła. Dostęp do n-tego elementu wektora powinien zawsze wynosić O (1).
Elliott
@lashgar Próbowałem tego z tablicą, ale nie udało się. Czy to działa w przypadku tablicy?
era s'q
@ eras'q, wypróbowany z gcc 7.5.0na Ubuntu 18.04 i działa w przypadku tablicy w ten sam sposób.
lashgar
17

Iteratory wektora to iteratory o dostępie swobodnym, co oznacza, że ​​wyglądają i działają jak zwykłe wskaźniki.

Możesz uzyskać dostęp do n-tego elementu, dodając n do iteratora zwróconego przez begin()metodę kontenera lub możesz użyć operatora [].

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

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

Alternatywnie możesz użyć funkcji zaawansowanej, która działa ze wszystkimi rodzajami iteratorów. (Trzeba by się zastanowić, czy naprawdę chcesz wykonać „dostęp losowy” z iteratorami o dostępie innym niż losowy, ponieważ może to być kosztowne).

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

std::advance(it, 5);
int sixth = *it;
Wujek Ben
źródło
1
Możesz użyć również advancedla iteratorów o swobodnym dostępie lub iteratorów o nieznanej kategorii, ponieważ w tym przypadku gwarantujemy, że będą działać w stałym czasie. Dlatego też iteratory zdefiniowane przez użytkownika powinny być poprawnie oznakowane.
Steve Jessop
Rzeczywiście, ale advancejest to naprawdę denerwujące w użyciu (ze względu na użycie parametru out), jeśli wiesz, że masz do czynienia z iteratorami o swobodnym dostępie. Poleciłbym tylko w kodzie ogólnym, a jeśli nie jest używany często (jeśli algorytm nie obsługuje dobrze iteratorów o dostępie innym niż losowy, niech tak będzie - na przykład std::sort mógłby posortować, std::listale tak nie jest, ponieważ byłby absurdalnie nieefektywny ).
UncleBens
Jasne, klasycznym przykładem byłoby, gdyby twój algorytm faktycznie potrzebował tylko InputIteratora, ale z jakiegoś powodu czasami przeskakuje do przodu, więc chcesz, aby był bardziej wydajny, jeśli iterator ma dostęp losowy. Nie warto ograniczać swojego algorytmu do losowego dostępu tylko za pomocą operator+. Ale pytanie dotyczyło bezpośrednio wektora, więc nie ma nic złego w pierwszej części twojej odpowiedzi. Pomyślałem po prostu, że druga część może oznaczać „nie można używać zaawansowanych z iteratorami o swobodnym dostępie, nawet jeśli chcesz” komuś, kto nigdy advancewcześniej nie widział .
Steve Jessop
OK, przeredagowałem ten bit i podałem przykład z wektorem.
UncleBens
druga linia Vectorpowinna być pisana małymi literami
Lei Yang
0

Oto przykład dostępu do ithindeksu a std::vectorprzy użyciu std::iteratorpętli wewnątrz pętli, która nie wymaga inkrementacji dwóch iteratorów.

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

Bez pętli for

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

i atmetodą:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
hmofrad
źródło