Pour compliquer l'exemple standard

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:

  1. 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.
  2. 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; } 
  3. 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; } 
  4. 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?

  1. 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).
  2. 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.
  3. 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.
  4. Functors - un outil puissant qui vous permet d'implémenter des fonctions avec "mémoire" ou un comportement supplémentaire
  5. ... 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:

  1. Stepanov Al. Lee Meng, La bibliothèque de modèles standard, 1995
  2. Référence du RPC, copie
  3. CPP Reference, inserter
  4. Référence CPP, istream_iterator
  5. Référence CPP, ostream_iterator
  6. Foncteur Wiki

Source: https://habr.com/ru/post/fr433218/


All Articles