Complicando el ejemplo estándar

La biblioteca estándar de C ++ ofrece no solo un conjunto de clases, sino que también determina cómo se escriben los programas. Este artículo analiza los requisitos generales para implementar programas que utilizan STL.

Considere la siguiente tarea:
Lea una matriz de enteros separados por espacios en blanco del archivo input.txt. Ordénelos y escriba en output.txt

Puedes escribir la siguiente solución:

#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; } 

Algunas palabras sobre la "magia" en el código:

  • Una de las bases de la biblioteca son los iteradores, así como los medios intervalos que definen. Por semántica (lectura - por comportamiento) coinciden con punteros. Es decir, el operador de desreferenciación * le devolverá el elemento al que se refiere el iterador, ++ traducirá el iterador al siguiente elemento. En particular, cualquier contenedor está representado por sus iteradores finales [begin, end), donde begin apunta al primer elemento, end - para el último ;
  • Algoritmos que funcionan con contenedores, ya que los parámetros toman el principio y el final del contenedor (o parte del mismo);
  • El algoritmo de copia y copia simplemente reescribe elementos de un medio intervalo a otro. Si no se asigna memoria en el contenedor de destino, el comportamiento es impredecible [ copia ];
  • La función inserter inserta un valor en el contenedor antes del iterador especificado [ inserter ]
  • istream_iterator y ostream_iterator proporcionan acceso de estilo contenedor a las transmisiones [ istream_iterator , ostream_iterator ]

Este ejemplo es realmente bastante simple. Sin embargo, puede ayudarnos a resolver el siguiente problema:
El archivo input.txt contiene una lista que contiene información sobre las personas: apellido, nombre, edad (cada línea es un registro, los datos están separados por un espacio). Lea estos datos en una matriz, ordénelos por edad y escriba en el archivo output.txt. Mostrar información sobre una persona cuya edad es mayor de 20 años pero menor de 25 años.
En principio, la solución será casi la misma. Sin embargo, para guardar la decisión, es necesario llevar a cabo un trabajo preparatorio, a saber:

  1. Declarar una estructura de datos. - puedes definir algo útil, pero en un caso particular, struct es suficiente:

     struct man{ std::string firstname, secondname; size_t age; }; 

    Le recomiendo que considere la implementación de constructores de copia, con parámetros predeterminados y el operador de copia. Con el desarrollo posterior del proyecto, definitivamente los necesitará.
  2. Operadores de E / S de sobrecarga : estos operadores son manipulados por iteradores sobre subprocesos. De todos modos, es más común usarlos.

     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. Defina las reglas para ordenar objetos : esto ya es una gran extensión: puede anular el operador <, puede describir una función, functor o expresión lambda. En este caso, use la función.

     bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; } 
  4. Defina una regla para seleccionar objetos : una vez más, una selección bastante grande de implementaciones. Esta vez, permita que haya un functor (un objeto de la clase en la que se define el operador de paréntesis [ functor ]) al que puede pasar el rango de edad:

     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); } }; 

    Presta atención al constructor del functor, de esta manera podemos personalizar su comportamiento.

Bueno, en realidad, el punto de entrada al programa:

 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; } 

Como puede ver, los cambios en la función principal son mínimos y afectan solo el tipo de elementos vectoriales. Además agregó una llamada al algoritmo copy_if. Este útil algoritmo apareció con el estándar C ++ 11; copia elementos de un contenedor a solo aquellos elementos que satisfacen la condición.

¿Qué conclusiones se pueden sacar de esto?

  1. Conocer y utilizar activamente los algoritmos de la biblioteca estándar acelera significativamente el desarrollo (más precisamente, conduce al automatismo).
  2. Declarar varios constructores y operadores de copia para estructuras de datos es útil. Se utilizan en diversas situaciones, en particular al insertar elementos en contenedores.
  3. Para mayor comodidad, puede sobrecargar los operadores de entrada y salida, así como el operador de comparación y el operador de pedidos.
  4. Functors: una herramienta poderosa que le permite implementar funciones con "memoria" o comportamiento adicional
  5. ... tal vez un poco más ...

¡Gracias por aguantar!

Todo el código del programa:

un_ejemplo.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; } 

Bibliografía

  1. Stepanov Al. Lee Meng, The Standard Template Library, 1995
  2. Referencia de CPP, copia
  3. Referencia de CPP, insertador
  4. Referencia de CPP, istream_iterator
  5. Referencia de CPP, ostream_iterator
  6. Functor de Wiki

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


All Articles