Durchlaufen Sie einen C ++ - Vektor mit einer 'for'-Schleife

139

Ich bin neu in der C ++ - Sprache. Ich habe angefangen, Vektoren zu verwenden, und habe festgestellt, dass in dem gesamten Code, den ich sehe, um einen Vektor über Indizes zu iterieren, der erste Parameter der forSchleife immer auf dem Vektor basiert. In Java könnte ich so etwas mit einer ArrayList machen:

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

Gibt es einen Grund, warum ich das in C ++ nicht sehe? Ist es schlechte Praxis?

Flynn
quelle
1
Die for-Schleife ist keine Funktion, hat also keine Parameter (oder Argumente, die Sie übergeben). Meinst du so etwas std::vector<int>::size_type i = 0;oder vielleicht std::vector<int>::iterator it = vector.begin();?
Chris
Genau, alle Beispiele, die ich sehe, sind so geschrieben.
Flynn
4
In Java würde ich eine for-each-Schleife bevorzugen oder Iteratoren verwenden. Ziemlich genau wie C ++, obwohl die Syntax etwas anders ist.
Jesse Good
2
Mögliches Duplikat von Warum Iteratoren anstelle von Array-Indizes verwenden?
Johnsyweb
10
Die meisten Antworten hier gehen fälschlicherweise davon aus, dass das Q lautetstd::vector : Was ist der beste / kürzeste Weg, um darüber zu iterieren ? Das tatsächliche Q, das hier gefragt wird, lautet: Gibt es einen Grund, warum ich dies in C ++ nicht sehe? Ist es schlechte Praxis? aka Warum sehe ich in C ++ immer Code, der beim Durchlaufen Iteratoren verwendet std::vector?
Alok Save

Antworten:

92

Gibt es einen Grund, warum ich das in C ++ nicht sehe? Ist es schlechte Praxis?

Nein. Es ist keine schlechte Praxis, aber der folgende Ansatz verleiht Ihrem Code eine gewisse Flexibilität .

Normalerweise verwendet der Code zum Iterieren über Containerelemente vor C ++ 11 Iteratoren, etwa:

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

Dies liegt daran, dass der Code dadurch flexibler wird.

Alle Standardbibliothekscontainer unterstützen und bieten Iteratoren. Wenn Sie zu einem späteren Zeitpunkt der Entwicklung zu einem anderen Container wechseln müssen, muss dieser Code nicht geändert werden.

Hinweis: Das Schreiben von Code, der mit jedem möglichen Standardbibliothekscontainer funktioniert, ist nicht so einfach, wie es scheint.

Alok Speichern
quelle
25
Könnte mir bitte jemand erklären, warum Sie in diesem speziellen Fall / Code-Snippet Iteratoren über die Indizierung raten? Was ist diese "Flexibilität", von der Sie sprechen? Persönlich mag ich keine Iteratoren, sie blähen den Code auf - einfach mehr Zeichen, die für den gleichen Effekt eingegeben werden müssen. Vor allem, wenn Sie nicht verwenden können auto.
Violette Giraffe
8
@VioletGiraffe: Während der Verwendung von Iteratoren ist es schwierig, in bestimmten Fällen wie leeren Bereichen etwas falsch zu machen, und der Code ist ausführlicher. Natürlich ist es eine Frage oder Wahrnehmung und Wahl, so dass es endlos diskutiert werden kann.
Alok Save
8
Warum zeigen Sie nur, wie Sie den Iterator deklarieren, nicht aber, wie Sie ihn für die Schleife verwenden ...?
underscore_d
115

Der Grund, warum Sie eine solche Praxis nicht sehen, ist ziemlich subjektiv und kann keine eindeutige Antwort haben, da ich viele der Codes gesehen habe, die Ihre erwähnte Methode anstelle von iteratorStilcode verwenden.

Folgendes kann Gründe dafür sein, dass Menschen nicht über die vector.size()Art der Schleife nachdenken:

  1. Paranoid sein, size()wenn Sie jedes Mal in der Schleifenbedingung anrufen . Entweder ist es kein Problem oder es kann trivial behoben werden
  2. Bevorzugen std::for_each()Sie die forSchleife selbst
  3. Später Wechsel des Behälters aus std::vectorzu anderen (zB map, list) wird auch die Änderung des Schleifenmechanismus verlangen, weil nicht jeder Container Unterstützung size()Stil der Looping

C ++ 11 bietet eine gute Möglichkeit, sich durch die Container zu bewegen. Dies wird als "Bereich basierend auf Schleife" (oder "erweitert für Schleife" in Java) bezeichnet.

Mit wenig Code können Sie das Ganze durchlaufen (obligatorisch!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;
iammilind
quelle
12
Nur um einen kleinen Nachteil des Bereichs zu bemerken, der auf der Schleife basiert : Sie können ihn nicht mit verwenden #pragma omp parallel for.
Liborm
2
Ich mag die kompakte Version, weil weniger Code zu lesen ist. Sobald Sie die mentale Anpassung vorgenommen haben, ist es viel einfacher zu verstehen und Fehler fallen mehr auf. Dies macht es auch viel offensichtlicher, wenn eine nicht standardmäßige Iteration stattfindet, da ein viel größerer Codeabschnitt vorhanden ist.
Code Abominator
87

Die sauberste Art, einen Vektor zu durchlaufen, sind Iteratoren:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

oder (entspricht dem oben genannten)

for (auto & element : vector) {
    element.doSomething ();
}

Vor C ++ 0x müssen Sie auto durch den Iteratortyp ersetzen und Elementfunktionen anstelle der globalen Funktionen begin und end verwenden.

Das haben Sie wahrscheinlich gesehen. Im Vergleich zu dem von Ihnen erwähnten Ansatz besteht der Vorteil darin, dass Sie nicht stark von der Art der abhängig sind vector. Wenn Sie vectorzu einer anderen Klasse vom Typ "Sammlung" wechseln , funktioniert Ihr Code wahrscheinlich weiterhin. Ähnliches können Sie jedoch auch in Java tun. Konzeptionell gibt es keinen großen Unterschied. C ++ verwendet jedoch Vorlagen, um dies zu implementieren (im Vergleich zu Generika in Java). daher wird der Ansatz für alle Arten arbeiten , für die beginund endFunktionen definiert sind, auch für Nicht-Klasse - Typen wie statische Arrays. Siehe hier: Wie funktioniert die bereichsbasierte Funktion für einfache Arrays?

JohnB
quelle
5
Auto, freier Anfang / Ende sind auch C ++ 11. Außerdem sollten Sie in vielen Fällen ++ anstelle von ++ verwenden.
ForEveR
Ja, du hast Recht. Implementierung beginund endist jedoch ein Einzeiler.
JohnB
@ JohnB es ist ein mehr als einzeiliger, weil es auch für Arrays mit fester Größe funktioniert. autoauf der anderen Seite wäre ziemlich schwierig.
Juanchopanza
Wenn Sie es nur für Vektor benötigen, ist es ein Einzeiler.
JohnB
Das erste Beispiel ist jedoch irreführend, da es in C ++ 03 nicht funktioniert, während Ihre Formulierung dies nahelegt.
Juanchopanza
35

Der richtige Weg, dies zu tun, ist:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

Dabei ist T der Typ der Klasse innerhalb des Vektors. Wenn die Klasse beispielsweise CActivity war, schreiben Sie einfach CActivity anstelle von T.

Diese Art von Methode funktioniert auf jeder STL (nicht nur Vektoren, was etwas besser ist).

Wenn Sie weiterhin Indizes verwenden möchten, gehen Sie wie folgt vor:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}
DiGMi
quelle
ist nicht std::vector<T>::size_typeimmer size_t? Das ist der Typ, den ich immer dafür benutze.
Violette Giraffe
1
@VioletGiraffe Ich bin mir ziemlich sicher, dass Sie Recht haben (nicht wirklich überprüft), aber es ist besser, std :: vector <T> :: size_type zu verwenden.
DiGMi
8

Es gibt einige starke Gründe, Iteratoren zu verwenden, von denen einige hier erwähnt werden:

Ein späterer Containerwechsel macht Ihren Code nicht ungültig.

Wenn Sie also von einem std :: -Vektor zu einer std :: -Liste oder einem std :: -Satz wechseln, können Sie keine numerischen Indizes verwenden, um zu Ihrem enthaltenen Wert zu gelangen. Die Verwendung eines Iterators ist weiterhin gültig.

Laufzeitabfangen ungültiger Iteration

Wenn Sie Ihren Container in der Mitte Ihrer Schleife ändern, wird bei der nächsten Verwendung Ihres Iterators eine ungültige Iteratorausnahme ausgelöst.

Eddie Parker
quelle
1
Können Sie auf einige Artikel / Posts verweisen, in denen die obigen Punkte mit Beispielcode erläutert werden? wäre großartig! oder wenn Sie eine hinzufügen könnten :)
Anu
5

Ich war überrascht, dass niemand erwähnt hat, dass das Durchlaufen eines Arrays mit einem ganzzahligen Index es Ihnen leicht macht, fehlerhaften Code zu schreiben, indem Sie ein Array mit dem falschen Index abonnieren. Wenn Sie beispielsweise verschachtelte Schleifen mit iund jals Indizes haben, können Sie ein Array mit jund nicht falsch abonnieren iund somit einen Fehler in das Programm einfügen.

Im Gegensatz dazu sind die anderen hier aufgeführten Formen, nämlich die bereichsbasierte forSchleife und die Iteratoren, viel weniger fehleranfällig. Die Semantik der Sprache und der Typprüfungsmechanismus des Compilers verhindern, dass Sie versehentlich mit dem falschen Index auf ein Array zugreifen.

Diomidis Spinellis
quelle
4

Mit STL verwenden Programmierer das iteratorsDurchlaufen von Containern, da der Iterator ein abstraktes Konzept ist, das in allen Standardcontainern implementiert ist. Zum Beispiel std::listhat überhaupt keine operator [].

Für immer
quelle
3

Die Verwendung des Auto-Operators macht die Verwendung wirklich einfach, da Sie sich nicht um den Datentyp und die Größe des Vektors oder einer anderen Datenstruktur kümmern müssen

Iterierender Vektor mit auto und for loop

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

Ausgabe:

1 2 3 4 5

Sie können diese Methode auch verwenden, um Sätze und Listen zu iterieren. Mit Auto erkennt automatisch den Datentyp in der Vorlage verwendet und können Sie es verwenden. Also, auch wenn ich eine hatte vectordie stringoder chardie gleiche Syntax wird gut funktionieren

Hrishikesh
quelle
1

Die richtige Methode zum Iterieren der Schleife und zum Drucken ihrer Werte lautet wie folgt:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}
Nikhil Rai
quelle
1

Hier ist eine einfachere Möglichkeit, Werte im Vektor zu iterieren und zu drucken.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 
Akram Mohammed
quelle
0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
bashar
quelle