Die C ++ - Standardbibliothek bietet nicht nur eine Reihe von Klassen, sondern bestimmt auch, wie Programme geschrieben werden. Dieser Artikel beschreibt die allgemeinen Anforderungen für die Implementierung von Programmen mit STL.
Betrachten Sie die folgende Aufgabe:
Lesen Sie ein Array von Ganzzahlen, die durch Leerzeichen von der Datei input.txt getrennt sind. Sortieren Sie sie und schreiben Sie in output.txt
Sie können die folgende Lösung schreiben:
#include <vector> #include <algorithm> #include <fstream> int main(){ // input.txt std::ifstream fin("input.txt"); // output.txt std::ofstream fout("output.txt"); // std::vector<int> v; // , std::copy(std::istream_iterator<int>(fin), std::istream_iterator<int>(), std::inserter(v, v.end())); // std::sort(v.begin(), v.end()); // , std::copy(v.begin(), v.end(), std::ostream_iterator<int>(fout, " ")); return 0; }
Ein paar Worte zur "Magie" im Code:
- Eine der Grundlagen der Bibliothek sind Iteratoren sowie die von ihnen definierten Halbintervalle. Durch die Semantik (Read-by-Verhalten) stimmen sie mit Zeigern überein. Das heißt, der Dereferenzierungsoperator * gibt Ihnen das Element zurück, auf das sich der Iterator bezieht. ++ übersetzt den Iterator in das nächste Element. Insbesondere wird jeder Container durch seine Enditeratoren (begin, end) dargestellt, wobei begin auf das erste Element zeigt, end - für das letzte ;
- Algorithmen, die mit Containern als Parameter arbeiten, nehmen den Anfang und das Ende des Containers (oder eines Teils davon);
- Der Kopierkopieralgorithmus schreibt einfach Elemente von einem halben Intervall in ein anderes um. Wenn im Zielcontainer kein Speicher zugewiesen ist, ist das Verhalten nicht vorhersehbar [ Kopie ].
- Die Inserter-Funktion fügt vor dem angegebenen Iterator [ Inserter ] einen Wert in den Container ein.
- istream_iterator und ostream_iterator bieten Container-Zugriff auf Streams [ istream_iterator , ostream_iterator ]
Dieses Beispiel ist eigentlich ganz einfach. Er kann uns jedoch bei der Lösung des folgenden Problems helfen:
Die Datei input.txt enthält eine Liste mit Informationen zu Personen: Nachname, Vorname, Alter (jede Zeile ist ein Datensatz, Daten sind durch ein Leerzeichen getrennt). Lesen Sie diese Daten in ein Array, sortieren Sie sie nach Alter und schreiben Sie in die Datei output.txt. Zeigen Sie Informationen zu einer Person an, deren Alter mehr als 20, aber weniger als 25 Jahre beträgt.
Im Prinzip wird die Lösung fast dieselbe sein. Um die Entscheidung zu retten, müssen jedoch vorbereitende Arbeiten durchgeführt werden, nämlich:
- Deklarieren Sie eine Datenstruktur. - Sie können etwas Nützliches definieren, aber in einem bestimmten Fall reicht die Struktur aus:
struct man{ std::string firstname, secondname; size_t age; };
Ich empfehle dringend, dass Sie die Implementierung von Kopierkonstruktoren mit Standardparametern und dem Kopieroperator in Betracht ziehen. Mit der Weiterentwicklung des Projekts werden Sie sie definitiv brauchen. - Überlastungs-E / A- Operatoren - Diese Operatoren werden von Iteratoren über Threads bearbeitet. Wie auch immer, es ist üblicher, sie zu verwenden.
std::ostream& operator << (std::ostream& out, const man& p){ out << p.firstname << " " << p.secondname << " " << p.age; return out; } std::istream& operator >> (std::istream& in, man& p){ in >> p.firstname >> p.secondname >> p.age; return in; }
- Regeln für die Reihenfolge von Objekten definieren - Dies ist bereits eine große Fläche: Sie können den Operator <überschreiben, Sie können eine Funktion, einen Funktor oder einen Lambda-Ausdruck beschreiben. Verwenden Sie in diesem Fall die Funktion.
bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; }
- Definieren Sie eine Regel für die Auswahl von Objekten - Wieder eine ziemlich große Auswahl an Implementierungen. Diesmal soll es einen Funktor geben (ein Objekt der Klasse, in dem der Operator in Klammern [ Funktor ] definiert ist), an den Sie die Altersspanne übergeben können:
struct predicate{ size_t begin, end; predicate(int p1, int p2): begin(p1), end(p2) {} bool operator ()(const man& p){ return (p.age > begin) && (p.age < end); } };
Achten Sie auf den Konstruktor des Funktors - auf diese Weise können wir sein Verhalten anpassen.
Nun, eigentlich der Einstiegspunkt in das Programm:
int main(){ std::ifstream fin("input.txt"); std::ofstream fout("output.txt"); std::vector<man> v; std::copy(std::istream_iterator<man>(fin), std::istream_iterator<man>(), std::inserter(v, v.end())); std::sort(v.begin(), v.end(), comparator); std::copy_if(v.begin(), v.end(), std::ostream_iterator<man>(std::cout, "\n"), predicate(20, 25)); std::copy(v.begin(), v.end(), std::ostream_iterator<man>(fout, "\n")); return 0; }
Wie Sie sehen können, sind die Änderungen an der Hauptfunktion minimal und wirken sich nur auf den Typ der Vektorelemente aus. Außerdem wurde dem copy_if-Algorithmus ein Aufruf hinzugefügt. Dieser nützliche Algorithmus wurde mit dem C ++ 11-Standard erstellt und kopiert Elemente aus einem Container nur in die Elemente, die die Bedingung erfüllen.
Welche Schlussfolgerungen können daraus gezogen werden?- Das Wissen und die aktive Verwendung der Algorithmen der Standardbibliothek beschleunigt die Entwicklung erheblich (genauer gesagt, dies führt zu Automatismus).
- Das Deklarieren verschiedener Konstruktoren und Kopieroperatoren für Datenstrukturen ist nützlich. Sie werden in verschiedenen Situationen eingesetzt, insbesondere beim Einfügen von Elementen in Container.
- Der Einfachheit halber können Sie die Eingabe- und Ausgabeoperatoren sowie den Vergleichsoperator und den Bestelloperator überladen.
- Funktoren - ein leistungsstarkes Tool, mit dem Sie Funktionen mit "Speicher" oder zusätzlichem Verhalten implementieren können
- ... vielleicht noch mehr ...
Danke fürs Aushalten!
Alle Programmcodes:an_example.cpp #include <string> #include <vector> #include <fstream> #include <algorithm> #include <iostream> #include <iterator> struct man{ std::string firstname, secondname; size_t age; }; std::ostream& operator << (std::ostream& out, const man& p){ out << p.firstname << " " << p.secondname << " " << p.age; return out; } std::istream& operator >> (std::istream& in, man& p){ in >> p.firstname >> p.secondname >> p.age; return in; } bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; } struct predicate{ size_t begin, end; predicate(int p1, int p2): begin(p1), end(p2) {} bool operator ()(const man& p){ return (p.age > begin) && (p.age < end); } }; int main(){ std::ifstream fin("input.txt"); std::ofstream fout("output.txt"); std::vector<man> v; std::vector<man>::iterator i; std::copy(std::istream_iterator<man>(fin), std::istream_iterator<man>(), std::inserter(v, v.end())); std::sort(v.begin(), v.end(), comparator); std::copy_if(v.begin(), v.end(), std::ostream_iterator<man>(std::cout, "\n"), predicate(20, 25)); std::copy(v.begin(), v.end(), std::ostream_iterator<man>(fout, "\n")); return 0; }
Bibliographie:- Stepanov Al. Lee Meng, The Standard Template Library, 1995
- CPP-Referenz, Kopie
- CPP-Referenz, Inserter
- CPP-Referenz, istream_iterator
- CPP-Referenz, ostream_iterator
- Wiki-Funktor