La bibliothèque standard C ++ propose non seulement un ensemble de classes, mais détermine également comment les programmes sont écrits. Cet article décrit les exigences générales pour l'implémentation de programmes utilisant STL.
Considérez la tâche suivante:
Lisez un tableau d'entiers séparés par des espaces dans le fichier input.txt. Triez-les et écrivez dans output.txt
Vous pouvez écrire la solution suivante:
#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; }
Quelques mots sur la "magie" du code:
- L'un des fondements de la bibliothèque est les itérateurs, ainsi que les demi-intervalles qu'ils définissent. Par sémantique (lecture par comportement), elles coïncident avec des pointeurs. Autrement dit, l'opérateur de déréférencement * vous renverra l'élément auquel l'itérateur fait référence, ++ traduira l'itérateur en l'élément suivant. En particulier, tout conteneur est représenté par ses itérateurs de fin [début, fin), où début pointe vers le premier élément, fin - pour le dernier ;
- Algorithmes qui fonctionnent avec des conteneurs, car les paramètres prennent le début et la fin du conteneur (ou une partie de celui-ci);
- L'algorithme de copie copie réécrit simplement les éléments d'un demi-intervalle à l'autre. Si aucune mémoire n'est allouée dans le conteneur cible, le comportement est imprévisible [ copie ];
- La fonction d'insertion insère une valeur dans le conteneur avant l'itérateur spécifié [ inséreur ]
- istream_iterator et ostream_iterator fournissent un accès de style conteneur aux flux [ istream_iterator , ostream_iterator ]
Cet exemple est en fait assez simple. Cependant, il peut nous aider à résoudre le problème suivant:
Le fichier input.txt contient une liste contenant des informations sur les personnes: nom, prénom, âge (chaque ligne est un enregistrement, les données sont séparées par un espace). Lisez ces données dans un tableau, triez-les par âge et écrivez dans le fichier output.txt. Afficher des informations sur une personne dont l'âge est supérieur à 20 ans mais inférieur à 25 ans.
En principe, la solution sera presque la même. Cependant, pour sauvegarder la décision, il est nécessaire d'effectuer des travaux préparatoires, à savoir:
- Déclarez une structure de données. - vous pouvez définir quelque chose d'utile, mais dans un cas particulier, struct suffit:
struct man{ std::string firstname, secondname; size_t age; };
Je vous recommande fortement de considérer l'implémentation de constructeurs de copie, avec des paramètres par défaut et l'opérateur de copie. Avec le développement du projet, vous en aurez certainement besoin. - Opérateurs d' E / S de surcharge - ces opérateurs sont manipulés par les itérateurs sur les threads. Quoi qu'il en soit, il est plus courant de les utiliser.
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; }
- Définir les règles de classement des objets - C'est déjà une grande étendue: vous pouvez surcharger l'opérateur <, vous pouvez décrire une fonction, un foncteur ou une expression lambda. Dans ce cas, utilisez la fonction.
bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; }
- Définir une règle de sélection d'objets - Encore une fois, une sélection assez large d'implémentations. Cette fois, qu'il y ait un foncteur (un objet de la classe dans lequel l'opérateur [ functor ] entre parenthèses est défini) auquel vous pouvez passer la tranche d'âge:
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); } };
Faites attention au constructeur du foncteur - de cette façon, nous pouvons personnaliser son comportement.
Eh bien, en fait, le point d'entrée du programme:
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; }
Comme vous pouvez le voir, les modifications apportées à la fonction principale sont minimes et n'affectent que le type d'éléments vectoriels. De plus, un appel à l'algorithme copy_if a été ajouté. Cet algorithme utile est apparu avec la norme C ++ 11; il copie les éléments d'un conteneur vers uniquement les éléments qui satisfont la condition.
Quelles conclusions peut-on en tirer?- La connaissance et l'utilisation active des algorithmes de la bibliothèque standard accélèrent considérablement le développement (plus précisément, cela conduit à l'automatisme).
- La déclaration de divers constructeurs et opérateurs de copie pour les structures de données est utile. Ils sont utilisés dans diverses situations, notamment lors de l'insertion d'éléments dans des conteneurs.
- Pour plus de commodité, vous pouvez surcharger les opérateurs d'entrée et de sortie, ainsi que l'opérateur de comparaison et l'opérateur de commande.
- Functors - un outil puissant qui vous permet d'implémenter des fonctions avec "mémoire" ou un comportement supplémentaire
- ... peut-ĂŞtre un peu plus ...
Merci d'avoir enduré!
Tous les codes de programme: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, La bibliothèque de modèles standard, 1995
- Référence du RPC, copie
- CPP Reference, inserter
- Référence CPP, istream_iterator
- Référence CPP, ostream_iterator
- Foncteur Wiki