Complicando o exemplo padrão

A biblioteca padrão C ++ oferece não apenas um conjunto de classes, mas também determina como os programas são gravados. Este artigo discute os requisitos gerais para implementar programas usando STL.

Considere a seguinte tarefa:
Leia uma matriz de números inteiros separados por espaço em branco no arquivo input.txt. Classifique-os e escreva para output.txt

Você pode escrever a seguinte solução:

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

Algumas palavras sobre a "mágica" no código:

  • Um dos fundamentos da biblioteca são os iteradores, bem como os meios intervalos que eles definem. Pela semântica (comportamento de leitura), eles coincidem com os ponteiros. Ou seja, o operador de desreferenciação * retornará para você o elemento ao qual o iterador se refere, ++ converterá o iterador para o próximo elemento. Em particular, qualquer contêiner é representado por seus iteradores finais [begin, end), onde start aponta para o primeiro elemento, end - para o último ;
  • Algoritmos que funcionam com contêineres, pois os parâmetros levam o início e o fim do contêiner (ou parte dele);
  • O algoritmo copy copy copy simplesmente reescreve elementos de um meio intervalo para outro. Se nenhuma memória é alocada no contêiner de destino, o comportamento é imprevisível [ cópia ];
  • A função de inserção insere um valor no contêiner antes do iterador especificado [ inserter ]
  • istream_iterator e ostream_iterator fornecem acesso no estilo de contêiner aos fluxos [ istream_iterator , ostream_iterator ]

Este exemplo é realmente bastante simples. No entanto, ele pode nos ajudar a resolver o seguinte problema:
O arquivo input.txt contém uma lista contendo informações sobre pessoas: sobrenome, nome, idade (cada linha é um registro, os dados são separados por um espaço). Leia esses dados em uma matriz, classifique-os por idade e escreva no arquivo output.txt. Exiba informações sobre uma pessoa com mais de 20 anos e menos de 25 anos.
Em princípio, a solução será quase a mesma. No entanto, para salvar a decisão, é necessário realizar trabalhos preparatórios, a saber:

  1. Declare uma estrutura de dados. - você pode definir algo útil, mas em um caso específico, struct é suficiente:

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

    Eu recomendo fortemente que você considere a implementação de construtores de cópia, com parâmetros padrão e o operador de cópia. Com o desenvolvimento do projeto, você definitivamente precisará deles.
  2. Operadores de sobrecarga de E / S - esses operadores são manipulados por iteradores sobre encadeamentos. De qualquer forma, é mais comum usá-los.

     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. Definir as regras para ordenar objetos - Essa já é uma grande extensão: você pode substituir o operador <, pode descrever uma função, functor ou expressão lambda. Nesse caso, use a função

     bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; } 
  4. Definir uma regra para selecionar objetos - Novamente, uma seleção bastante grande de implementações. Desta vez, deixe existir um functor (um objeto da classe em que o operador [ functor ] de parênteses está definido) para o qual você pode passar a faixa etária:

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

    Preste atenção ao construtor do functor - desta forma, podemos personalizar seu comportamento.

Bem, na verdade, o ponto de entrada para o 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 você pode ver, as alterações na função principal são mínimas, afetando apenas o tipo de elementos do vetor. Além disso, foi adicionada uma chamada ao algoritmo copy_if. Esse algoritmo útil apareceu com o padrão C ++ 11; copia elementos de um contêiner para apenas aqueles que satisfazem a condição.

Que conclusões podem ser tiradas disso?

  1. Conhecer e usar ativamente os algoritmos da biblioteca padrão acelera significativamente o desenvolvimento (mais precisamente, isso leva ao automatismo).
  2. É útil declarar vários construtores e operadores de cópia para estruturas de dados. Eles são usados ​​em várias situações, principalmente ao inserir elementos em recipientes.
  3. Por conveniência, você pode sobrecarregar os operadores de entrada e saída, bem como o operador de comparação e o operador de pedidos.
  4. Functors - uma ferramenta poderosa que permite implementar funções com "memória" ou comportamento adicional
  5. ... talvez um pouco mais ...

Obrigado por resistir!

Todo o código do programa:

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

Bibliografia:

  1. Stepanov Al. Lee Meng, Biblioteca Padrão de Modelos, 1995
  2. Referência CPP, cópia
  3. Referência CPP, insersor
  4. Referência de CPP, istream_iterator
  5. Referência CPP, ostream_iterator
  6. Wiki functor

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


All Articles