Wie navigiere ich mit Iteratoren durch einen Vektor? (C ++)

105

Das Ziel besteht darin, auf das "n-te" Element eines Zeichenfolgenvektors anstelle des Operators [] oder der Methode "at" zuzugreifen. Soweit ich weiß, können Iteratoren verwendet werden, um durch Container zu navigieren, aber ich habe noch nie zuvor Iteratoren verwendet, und was ich lese, ist verwirrend.

Wenn mir jemand Informationen darüber geben könnte, wie dies erreicht werden kann, würde ich es begrüßen. Danke dir.

Kevin
quelle
Sind Vektoren nicht exklusiv für die STL von C ++? Ich werde es
trotzdem
kevin: vector ist ein Oberbegriff, der von jeder Sprache verwendet werden kann, insbesondere von mathematischen Begriffen wie Mathematica oder Matlab.
Gabe
@ Michael, ja haha, ich habe es nach Gabe's Kommentar bearbeitet.
Kevin

Antworten:

112

Sie müssen die Methode beginund endder vectorKlasse verwenden, die den Iterator zurückgibt, der auf das erste bzw. das letzte Element verweist.

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
quelle
5
Dies übersieht die Tatsache, dass std::vectorIteratoren mit wahlfreiem Zugriff vorhanden sind.
sbi
24
Unabhängig davon, ob Sie wissen, dass der Iteratortyp Direktzugriff ist oder nicht, besteht die "beste" Möglichkeit, einen Iterator in n Leerzeichen vorwärts zu bewegen, nicht darin, eine eigene Schleife zu schreiben, sondern aufzurufen std::advance(it, n). Es ist so definiert, dass es genau das tut, was Sie wollen, und es wird automatisch verwendet, it + nwenn der Iterator als Direktzugriff markiert ist, oder es wird die Schleife ausgeführt, wenn dies erforderlich ist.
Steve Jessop
61

In der Regel werden Iteratoren verwendet, um linear auf Elemente eines Containers zuzugreifen. Mit "Iteratoren mit wahlfreiem Zugriff" ist es jedoch möglich, auf jedes Element auf dieselbe Weise wie auf zuzugreifen operator[].

Um auf beliebige Elemente in einem Vektor zuzugreifen vec , können Sie Folgendes verwenden:

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

Das folgende Beispiel zeigt ein typisches Zugriffsmuster (frühere Versionen von C ++):

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

Der Vorteil der Verwendung des Iterators besteht darin, dass Sie dasselbe Muster auf andere Container anwenden können :

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

Aus diesem Grund ist es wirklich einfach, Vorlagencode zu erstellen, der unabhängig vom Containertyp gleich funktioniert . Ein weiterer Vorteil von Iteratoren besteht darin, dass nicht davon ausgegangen wird, dass sich die Daten im Speicher befinden. Beispielsweise könnte man einen Vorwärtsiterator erstellen, der Daten aus einem Eingabestream lesen kann oder der einfach Daten im laufenden Betrieb generiert (z. B. einen Bereichs- oder Zufallszahlengenerator).

Eine weitere Option mit std::for_eachund Lambdas:

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

Seit C ++ 11 können Sie autovermeiden, einen sehr langen, komplizierten Typnamen des Iterators wie zuvor angegeben (oder noch komplexer) anzugeben:

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

Außerdem gibt es für jede Variante eine einfachere:

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

Und schließlich müssen std::accumulateSie auch vorsichtig sein, ob Sie Ganzzahlen oder Gleitkommazahlen hinzufügen.

Michael Aaron Safyan
quelle
53

In C ++ - 11 können Sie Folgendes tun:

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

Variationen finden Sie hier: https://en.cppreference.com/w/cpp/language/range-for

Lashgar
quelle
4
WARUM HAT DAS NULL LIKES?! <3
jperl
3
@jperl Gepostet 8 Jahre zu spät. Wird die nächsten 8 Jahre brauchen, um genug Upvotes zu bekommen :)
Lashgar
@jperl, nun, die Antwort ist nicht zum Thema. Obwohl diese Schleifenfunktion nett ist, hilft es Ihnen nicht zu wissen, wann Sie sich am n-ten Element befinden, was die Frage des OP ist. Außerdem ist jede Antwort, die eine Komplexität der O (n) -Zeit erfordert, wie diese, sehr schlecht. Der Zugriff auf das n-te Element eines Vektors sollte immer O (1) sein.
Elliott
@lashgar Ich habe dies mit Array versucht, aber fehlgeschlagen. Funktioniert es für Array?
Ära s'q
@ eras'q, versucht mit gcc 7.5.0unter Ubuntu 18.04 und funktioniert für Array auf die gleiche Weise.
Lashgar
17

Die Iteratoren von Vector sind Iteratoren mit wahlfreiem Zugriff, was bedeutet, dass sie wie einfache Zeiger aussehen und sich anfühlen.

Sie können auf das n-te Element zugreifen, indem Sie dem von der Containermethode zurückgegebenen Iterator n hinzufügen begin(), oder Sie können den Operator verwenden [].

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

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

Alternativ können Sie die Erweiterungsfunktion verwenden, die mit allen Arten von Iteratoren funktioniert. (Sie müssten überlegen, ob Sie wirklich einen "Direktzugriff" mit Iteratoren ohne Direktzugriff durchführen möchten, da dies möglicherweise eine teure Sache ist.)

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

std::advance(it, 5);
int sixth = *it;
Onkel Bens
quelle
1
Sie können auch advanceIteratoren mit wahlfreiem Zugriff oder Iteratoren unbekannter Kategorie verwenden, da in diesem Fall ein konstanter Betrieb garantiert ist. Aus diesem Grund sollten benutzerdefinierte Iteratoren korrekt gekennzeichnet werden.
Steve Jessop
In der Tat, aber es advanceist wirklich ärgerlich zu verwenden (wegen der Verwendung von Out-Parametern), wenn Sie wissen, dass Sie mit Iteratoren mit wahlfreiem Zugriff zu tun haben. Würde nur in generischem Code empfehlen und wenn nicht viel verwendet (wenn der Algorithmus Iteratoren mit nicht wahlfreiem Zugriff nicht gut unterstützt, könnte es so sein - std::sort könnte beispielsweise eine sortieren std::list, aber nicht, weil es lächerlich ineffizient wäre ).
Onkel Bens
Sicher, das klassische Beispiel wäre, wenn Ihr Algorithmus nur tatsächlich braucht ein InputIterator, aber aus irgendeinem Grund überspringt es manchmal vor, so dass Sie es wollen effizienter sein , wenn der Iterator zufälligen Zugriff hat. Es lohnt sich nicht, Ihren Algorithmus auf den Direktzugriff nur mit zu beschränken operator+. Bei der Frage ging es jedoch explizit um Vektor, sodass am ersten Teil Ihrer Antwort nichts auszusetzen ist. Ich dachte nur, der zweite Teil könnte bedeuten, dass Sie "Advance nicht mit Iteratoren mit wahlfreiem Zugriff verwenden können, selbst wenn Sie möchten" für jemanden, der es noch nie advancezuvor gesehen hat.
Steve Jessop
OK, habe dieses Bit umformuliert und das Beispiel mit einem Vektor angegeben.
Onkel Bens
zweite Zeile Vectorsollte Kleinbuchstaben sein
Lei Yang
0

Hier ist ein Beispiel für den Zugriff auf den ithIndex von a std::vectormithilfe einer std::iteratorinnerhalb einer Schleife, für die keine Inkrementierung von zwei Iteratoren erforderlich ist.

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

Ohne for-Schleife

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

und mit atMethode:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
hmofrad
quelle