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:
- 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. - 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; }
- 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; }
- 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?- Conhecer e usar ativamente os algoritmos da biblioteca padrão acelera significativamente o desenvolvimento (mais precisamente, isso leva ao automatismo).
- É ú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.
- 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.
- Functors - uma ferramenta poderosa que permite implementar funções com "memória" ou comportamento adicional
- ... 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:- Stepanov Al. Lee Meng, Biblioteca Padrão de Modelos, 1995
- Referência CPP, cópia
- Referência CPP, insersor
- Referência de CPP, istream_iterator
- Referência CPP, ostream_iterator
- Wiki functor