Verbesserte FOR-Schleifen in C++

Lesezeit: 8 Minuten

Benutzer-Avatar
Daniel Gritzer

Ich wechsle von Java zu C++ und habe mich gefragt, ob C++ die erweiterten for-Schleifen enthält, die ich in Java verwendet habe, zum Beispiel:

int[] numbers = {1,2,3,4,5,6,7,8,9,10};
for (int item : numbers) {
  System.out.println("Count is: " + item);
}

Ist diese gleiche “Verknüpfung” in C++ möglich?

  • @Beginner: Jemand, der gerade zu C++ wechselt, weiß möglicherweise nicht, wie die C++-Versionierung funktioniert. Warum nicht tatsächlich eine Antwort geben, die den Unterschied in Bezug auf die Frage erklärt?

    – Millielch

    4. Dezember 2011 um 21:09 Uhr


  • @PaulManta Überhaupt nicht. Ich stimme zu, dass es besser sein könnte, alles vollständig zu erklären, aber das könnte auch getan werden, nachdem OP gesagt hat: “Ich weiß nicht, was C++XX bedeutet.” Ich wollte nur kurz antworten…..

    – Roman Byschko

    4. Dezember 2011 um 21:15 Uhr

  • @Paul: Es ist nicht selbstgefällig, eine Frage zu stellen, nur weil jemand anderes die Antwort vielleicht nicht kennt. Die Frage war vollkommen berechtigt.

    – Ildjarn

    4. Dezember 2011 um 22:00 Uhr

  • Ist dies das Äquivalent von foreach in C#?

    – Jameskind

    6. Dezember 2011 um 22:55 Uhr

  • so viele tolle Beispiele aus dieser Frage nett

    – pyCthon

    13. Dezember 2011 um 2:36 Uhr

Benutzer-Avatar
pmr

C++11 tut es. Sie werden bereichsbasierte fors genannt. Denken Sie daran, dass Sie den Typ als Referenz oder als Referenz auf const qualifizieren sollten.

Die Problemumgehung für C++03 ist BOOST_FOR_EACH oder boost::bind in Kombination mit std::for_each. Mit Boost.Lambda sind noch ausgefallenere Dinge möglich. Sollten Sie in der Stimmung sein, sich selbst oder Ihre Mitarbeiter zu frustrieren, empfehle ich die veralteten Ordner std::bind1st und std::bind2nd.

Hier ist ein Beispielcode:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <boost/lambda/lambda.hpp>
#include <functional>    

int main()
{
  int i = 0;
  std::vector<int> v;
  std::generate_n(std::back_inserter(v), 10, [&]() {return i++;});

  // range-based for
  // keep it simple
  for(auto a : v)
    std::cout << a << " ";
  std::cout << std::endl;

  // lambda
  // i don't like loops
  std::for_each(v.begin(), v.end(), [](int x) { 
      std::cout << x << " ";
    });
  std::cout << std::endl;

  // hardcore
  // i know my lib
  std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, " "));
  std::cout << std::endl;


  // boost lambda
  // this is what google came up with
  // using for the placeholder, otherwise this looks weird
  using namespace boost::lambda;
  std::for_each(v.begin(), v.end(), std::cout << _1 << " ");
  std::cout << std::endl;

  // fold
  // i want to be a haskell programmer
  std::accumulate(v.begin(), v.end(), std::ref(std::cout), 
                  [](std::ostream& o, int i) -> std::ostream& { return o << i << " "; });

  return 0;
}

  • // abfällige Kommentare sind urkomisch, +1

    – Nikolaus Palko

    6. Dezember 2011 um 15:19 Uhr

  • +1. Ich habe es mir nicht vorgestellt std::accumulate könnten so genutzt werden. Nicht, dass ich es in meinem Code verwenden werde, aber ich mochte seine Verwendung hier.

    – Nawaz

    9. Oktober 2013 um 16:39 Uhr


Benutzer-Avatar
jrok

In C++11, wenn Ihr Compiler es unterstützt, ja. Es heißt bereichsbasiert für.

std::vector<int> v;

// fill vector

for (const int& i : v) { std::cout << i << "\n"; }

Es funktioniert für Arrays im C-Stil und jeden Typ, der Funktionen hat begin() und end() die Iteratoren zurückgeben. Beispiel:

class test {
    int* array;
    size_t size;
public:
    test(size_t n) : array(new int[n]), size(n)
    {
        for (int i = 0; i < n; i++) { array[i] = i; }
    }
    ~test() { delete [] array; }
    int* begin() { return array; }
    int* end() { return array + size; }
};

int main()
{
    test T(10);
    for (auto& i : T) {
        std::cout << i;   // prints 0123456789
    }
}

  • Ich verstehe nicht, wie diese Antwort besser ist als mein vollständiges Kompendium zum Iterieren einer Sequenz. 😉

    – pmr

    4. Dezember 2011 um 21:41 Uhr

  • Da haben Sie sicherlich Recht, und ich stehe korrigiert und überzeugt da.

    – pmr

    4. Dezember 2011 um 21:46 Uhr

  • @Merlin Ich kann hier keine Hypothesen sehen. Nur Fakten. Und diese zeigen deutlich, dass es unzählige Möglichkeiten gibt, etwas in verschiedenen Versionen von C++ zu drucken, einige mehr oder weniger bequem und jeder Weg sollte einem Programmierer bekannt sein. Der, der verwendet accumulate ausgeschlossen.

    – pmr

    5. Dezember 2011 um 1:31 Uhr

  • Sein Verhalten ist nahezu identisch mit einer for-Schleife in Python.

    – ncmathsadist

    5. Dezember 2011 um 2:12 Uhr

  • @pmr – FYI, Occams Rasiermesser gilt nicht nur für Hypothesen, sondern für jede Situation, in der Sie die Komplexität von etwas reduzieren können, ohne Inhalt zu verlieren.

    – dezent

    5. Dezember 2011 um 2:33 Uhr

Benutzer-Avatar
Roman Byschko

In C++03 gibt es eine solche Möglichkeit nicht. Der neue Standard (C++11) hat es jedoch. Siehe Beispiel (entnommen aus Wikipedia):

int my_array[5] = {1, 2, 3, 4, 5};
for (int &x : my_array) {
    x *= 2;
}

Erwägen Sie auch die Verwendung std::vector<int> anstelle eines gewöhnlichen Arrays. Dies ist eine C++-Analogie für C-Datentypen, die das Leben einfacher macht.

  • Wie würde es wissen, wie lang das Array ist?

    – Lukasz Madon

    4. Dezember 2011 um 22:00 Uhr

  • @lukas Der Compiler würde es wissen. Sie können es auch selbst berechnen int arr_len = sizeof(my_array) / sizeof(my_array[0]);

    – Roman Byschko

    4. Dezember 2011 um 22:19 Uhr

  • mit anderen Worten, ich kann sie nur für “Stapel” -Arrays verwenden? kein Zeiger auf Array?

    – Lukasz Madon

    4. Dezember 2011 um 22:41 Uhr

  • @Lukas Ja. Aber werfen Sie einen Blick auf dieses stackoverflow.com/a/8378615/1008481 auch, es gibt andere Fälle, in denen Sie es verwenden können.

    – Roman Byschko

    4. Dezember 2011 um 22:48 Uhr


Benutzer-Avatar
Andreas Rasmussen

Ja und nein.

1. Lokales Array: Nein, aber Sie können die Größe leicht finden

Wenn Sie ein lokales Array haben (int numbers[4] = {1, 2, 3, 4];) dann kannst du das machen size = sizeof(numbers) / sizeof(int).

2. Zeiger auf Array: Überhaupt nicht, Sie müssen die Größe separat herumreichen

Wenn Sie einen Zeiger auf ein Array (int* numbers = new int[4];), dann können Sie die Größe nicht herausfinden, es sei denn, Sie verfolgen sie selbst. (oder wenn es im Fall einer Wechselstromzeichenfolge nullterminiert ist, aber dann müssen Sie es durchlaufen, was eine lineare Laufzeit ist …)

Beachten Sie, dass Zeiger auf Array meiner Meinung nach nicht die richtige Terminologie ist. Sie haben wirklich nur einen Zeiger auf das erste Element des Arrays, aber es wurde Platz für mehrere Werte zugewiesen. Nicht sicher, wie das heißt. Vielleicht nur ein Hinweis?

3. STL-Container: Ja, und Sie können mithilfe von Iteratoren für Schleifen zaubern oder einfach Indizes verwenden, indem Sie die Größe ermitteln

Wenn Sie einen Vektor haben (std::vector<int> v(3, 0);) dann können Sie es auf folgende Weise durchlaufen:

C++11:

auto it = v.begin();
for (auto it = v.begin(); it != v.end(); it++)
{
    UseElement(*it);
}

Oder anscheinend (auch C++11, danke jrok):

for (const int& i : v) { UseElement(i); }

C++ (vor 11):

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

Oder mit Indizes:

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

Darüber hinaus können Sie Funktionszeiger oder Funktoren mit STL-Containern verwenden, indem Sie den std-Algorithmus for_each (#include <algorithm>) so:

void foo(int i)
{
    std::cout << i;
}

{
    std::for_each(myvector.begin(), myvector.end(), foo);
}

Andere haben bereits erwähnt, dass dieser Schleifenstil in C++11 hinzugefügt wurde. C++11 ist jedoch noch besser:

for (auto const& item: numbers)
{
  std::cout << "Count is: " << item << '\n';
}

Wenn Sie also später den Elementtyp von ändern numbers aus int zu longoder sogar zu einigen bigint Klasse, die Sie selbst geschrieben haben, müssen Sie diese for-Schleife nicht ändern überhaupt.

Im alten Standard C++03 (aus dem Jahr 2003) hat die Sprache keine eingebaute Unterstützung für diese Art von for-Schleife. Es gibt einige Kunstgriffe, die Sie mit Boost verwenden können, aber meiner Meinung nach lohnt es sich nicht, eine ganz neue Bibliothek für diese kleine Komfortfunktion hinzuzufügen.

Im neuen Standard C++11 (der erst letzten Sommer veröffentlicht wurde) ist dies möglich; die syntax sieht so aus:

MyType array[] = { ... }
for (MyType& x : array) {
    ...
}

Beachten Sie, dass ich verwende MyType& xnicht MyType x. In Java ist alles eine Referenz. In C++ müssen Referenzen explizit sein, und Sie deklarieren sie mit &. Wenn Sie keine Referenzen verwenden, kopiert die for-Schleife jedes Element des Arrays hinein x (was teuer sein könnte).

Allerdings wird C++11 von den meisten Compilern noch nicht vollständig unterstützt. Ich denke, dass Visual C++ von Microsoft diese Funktion unterstützt, bin mir aber nicht sicher.

Benutzer-Avatar
Aaron McDaid

Ich finde dieses einfache Makro sehr nützlich. Die überwiegende Mehrheit meiner for Schleifen beinhalten das Iterieren über einen STL-Container:

#define For(it, container) for( typeof((container).begin()) it = (container).begin(); it != (container).end(); ++it)

Ein Beispiel:

vector<int> vector_of_ints;
... // initialize it somehow
For(integer, vector_of_ints) {
    cout << *integer << endl;
}

Dabei sind zwei Dinge zu beachten: Erstens ist es ein Iterator und muss daher dereferenziert werden. Und zweitens der zweite Parameter zum For wird mehrfach ausgewertet. Ich habe mit anderen Ansätzen gespielt, aber ich komme immer wieder auf die Einfachheit zurück.

  • Beachten Sie, dass der typeof-Operator kein C++-Standard ist.

    – Firas Assaad

    7. Dezember 2011 um 8:46 Uhr

1130920cookie-checkVerbesserte FOR-Schleifen in C++

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy