493 Stimmen

Iteration über std::vector: Vorzeichenlose vs. vorzeichenbehaftete Indexvariable

Was ist der richtige Weg, um über einen Vektor in C++ zu iterieren?

Betrachten Sie diese beiden Codefragmente, dieses hier funktioniert gut:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

und dieses hier:

for (int i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

was dazu führt Warnung: Vergleich zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlausdrücken.

Die unsigned Variable sieht für mich ein wenig beängstigend aus und ich weiß, dass unsigned Variablen gefährlich sein können, wenn sie nicht korrekt verwendet werden, also - ist das korrekt?

11 Stimmen

Die ununterzeichnete ist korrekt, weil polygon.size() vom Typ unsigned ist. Unsigned bedeutet immer positiv oder 0. Das ist alles, was es bedeutet. Wenn die Variable immer nur für Zähler verwendet wird, dann ist unsigned die richtige Wahl.

0 Stimmen

Um die Warnung bezüglich signed/unsigned zu lösen, ersetzen Sie einfach int durch uint (unsigned int) bei der Deklaration von i.

3 Stimmen

@AdamBruss .size() ist nicht vom Typ unsigned a.k.a. unsigned int. Es ist vom Typ std::size_t.

858voto

Johannes Schaub - litb Punkte 479831

Für die Umkehrung siehe diese Antwort.

Das Vorwärtsiterieren ist fast identisch. Ändern Sie einfach die Iteratoren / tauschen Sie das Dekrement durch das Inkrement aus. Sie sollten Iteratoren bevorzugen. Einige Leute sagen Ihnen, Sie sollen std::size_t als den Indexvariablentyp verwenden. Das ist jedoch nicht tragbar. Verwenden Sie immer den size_type typedef des Containers (Während Sie sich bei der Vorwärtsiteration nur mit einer Konvertierung behelfen könnten, könnte es tatsächlich bei der Rückwärtsiteration schiefgehen, wenn Sie std::size_t verwenden, falls std::size_t breiter ist als der typedef von size_type):


Verwendung von std::vector

Verwendung von Iteratoren

for(std::vector::iterator it = v.begin(); it != v.end(); ++it) {
    /* std::cout << *it; ... */
}

Wichtig ist, verwenden Sie immer die Präfix-Inkrementform für Iteratoren, deren Definitionen Sie nicht kennen. Das stellt sicher, dass Ihr Code so generisch wie möglich läuft.

Verwendung von Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

Verwendung von Indizes

for(std::vector::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Verwendung von Arrays

Verwendung von Iteratoren

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Verwendung von Range C++11

for(auto const& value: a) {
     /* std::cout << value; ... */

Verwendung von Indizes

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Lesen Sie in der Antwort zum Rückwärtsiterieren, welches Problem der Ansatz mit sizeof hervorrufen kann.

0 Stimmen

Die Größe und Art der Zeiger: Wenn Sie difference_type verwenden, könnte dies portabler sein. Versuchen Sie iterator_traits::difference_type. Das ist zwar eine lange Deklaration, aber es ist portabler...

0 Stimmen

Wilhelmtell, wofür sollte ich difference_type verwenden? sizeof ist definiert, um size_t zurückzugeben :) ich verstehe dich nicht. Wenn ich Pointer voneinander subtrahieren würde, wäre difference_type die richtige Wahl.

0 Stimmen

Die Iteration über Arrays mit der Technik, die Sie in diesem Beitrag erwähnt haben, funktioniert nicht, wenn die Iteration in einer Funktion an einem Array durchgeführt wird, das an diese Funktion übergeben wurde. Da sizeof Array nur die Größe des Zeigers zurückgibt.

180voto

kratenko Punkte 7012

Vier Jahre vergingen, Google gab mir diese Antwort. Mit dem Standard C++11 (aka C++0x) gibt es tatsächlich eine neue angenehme Möglichkeit, dies zu tun (zum Preis der Rückwärtskompatibilität zu brechen): das neue auto Schlüsselwort. Es erspart Ihnen die Qual, den Typ des Iterators explizit angeben zu müssen (den Vektortyp erneut wiederholen), wenn es offensichtlich ist (für den Compiler), welchen Typ zu verwenden. Mit v als Ihrem vector können Sie so etwas tun:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C++11 geht sogar noch weiter und bietet Ihnen eine spezielle Syntax zum Iterieren über Sammlungen wie Vektoren. Es entfernt die Notwendigkeit, Dinge zu schreiben, die immer gleich sind:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

Um dies in einem funktionierenden Programm zu sehen, erstellen Sie eine Datei auto.cpp:

#include 
#include 

int main(void) {
    std::vector v = std::vector();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

Zum Zeitpunkt des Verfassens dieses Textes müssen Sie dies beim Kompilieren mit g++ normalerweise angeben, um mit dem neuen Standard zu arbeiten, indem Sie eine zusätzliche Flagge angeben:

g++ -std=c++0x -o auto auto.cpp

Jetzt können Sie das Beispiel ausführen:

$ ./auto
17
12
23
42

Bitte beachten Sie, dass die Anweisungen zum Kompilieren und Ausführen spezifisch für den gnu c++ Compiler unter Linux sind, das Programm sollte plattformunabhängig sein (und Compiler-unabhängig).

9 Stimmen

C++11 gibt Ihnen for (auto& val: vec)

1 Stimmen

@flexo Danke, ich weiß nicht, wie ich das vergessen konnte. Ich mache anscheinend nicht genug C++. Konnte nicht glauben, dass es etwas so Praktisches gibt (dachte eigentlich, das sei JavaScript-Syntax). Ich habe die Antwort geändert, um das einzuschließen.

0 Stimmen

Deine Antwort ist sehr nett. Es ist ärgerlich, dass die Standardversion von g++ in verschiedenen Betriebssystem-Entwicklungskits unter 4.3 liegt, was dazu führt, dass es nicht funktioniert.

43voto

paxos1977 Punkte 143743

In dem spezifischen Fall in Ihrem Beispiel würde ich die STL-Algorithmen verwenden, um dies zu erreichen.

#include  

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

Für einen allgemeineren, aber immer noch recht einfachen Fall würde ich folgendes verwenden:

#include 
#include 

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

38voto

Polat Tuzla Punkte 1893

Regarding Johannes Schaub's answer:

for(std::vector::iterator it = v.begin(); it != v.end(); ++it) { 
...
}

Dies funktioniert möglicherweise bei einigen Compilern, aber nicht bei gcc. Das Problem hier ist die Frage, ob std::vector::iterator ein Typ, eine Variable (Element) oder eine Funktion (Methode) ist. Wir erhalten den folgenden Fehler bei gcc:

In der Memberfunktion "void MyClass::myMethod()":
Fehler: Erwartetes `;' vor "it"
Fehler: "it" wurde in diesem Geltungsbereich nicht deklariert
In der Memberfunktion "void MyClass::sort() [mit T = MyClass]":
Erstellt von 'void MyClass::run() [mit T = MyClass]'
Hier instanziiert
Abhängiger Name "std::vector >::iterator" wird als Nicht-Typ geparst, aber die Instantiierung ergibt einen Typ
Hinweis: Schreibe "typename std::vector >::iterator", wenn ein Typ gemeint ist

Die Lösung besteht darin, das Schlüsselwort 'typename' wie folgt zu verwenden:

typename std::vector::iterator it = v.begin();
for( ; it != v.end(); ++it) {
...

2 Stimmen

Du solltest darauf hinweisen, dass dies nur gilt, wenn T ein Vorlagenargument ist und somit der Ausdruck std::vector::iterator ein abhängiger Name ist. Damit ein abhängiger Name als Typ interpretiert wird, muss er durch das Schlüsselwort typename vorangestellt werden, wie die Diagnose anzeigt.

17voto

Jasper Bekkers Punkte 6545

Ein Aufruf von vector::size() gibt einen Wert vom Typ std::vector::size_type zurück, nicht int, unsigned int oder ähnliches.

Außerdem wird in C++ im Allgemeinen die Iteration über einen Container mit Iteratoren durchgeführt, so wie hier.

std::vector::iterator i = polygon.begin();
std::vector::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Wo T der Datentyp ist, den Sie im Vektor speichern.

Oder unter Verwendung der verschiedenen Iterationsalgorithmen (std::transform, std::copy, std::fill, std::for_each usw.).

0 Stimmen

Iteratoren sind im Allgemeinen eine gute Idee, obwohl ich bezweifle, dass es notwendig ist, "end" in einer separaten Variablen zu speichern und alles innerhalb einer for(;;) Anweisung durchgeführt werden kann.

1 Stimmen

Ich weiß, dass begin() und end() eine konstante Amortisationszeit haben, aber ich finde es im Allgemeinen lesbarer, als alles in eine Zeile zu quetschen.

3 Stimmen

Sie können das for in separate Zeilen aufteilen, um die Lesbarkeit zu verbessern. Das Deklarieren von Iteratoren außerhalb der Schleife bedeutet, dass Sie für jede Schleife über Container unterschiedlicher Typen einen anderen Iterator-Namen benötigen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X