Upload
internet
View
107
Download
0
Embed Size (px)
Citation preview
Funções definidas pelo usuário
STL oferece a possibilidade de que o usuário escreva seus próprios algoritmos para processar os elementos de coleções
#include <iostream>
template<classe T>void PRINT_ELEMENTS( const T& col, const char* opcional = “”{
typename T::const_iterator pos;
std::cout << opcional; for (pos = col.begin(); pos != col.end(); ++pos)
std::cout << *pos << ‘ ‘;std::cout << endl;
}
Algoritmos definidos pelo usuário
#include <set>typedef sdt::set<int> IntSet;
IntSet col;col.insert(3); col.insert(1); col.insert(4);col.insert(1); col.insert(2); col.insert(5);
PRINT_ALL_ELEMENTS(col, “Set: “);
Funções como argumentos
Vários algoritmos permitem a passagem de funções definidas pelo usuário como parâmetros. Essas serão chamadas pelo algoritmo
void print(int elem) { cout << elem << ‘ ‘ ; }
int main() {
vector<int> col;
for (int i=1; i<=9; ++i) col.push_back(i);
for_each(col.begin(), col.end(), print);
cout << endl;}
Funções como argumentos
Algumas vezes são requeridos, outras opcionais. Podem especificar critérios de busca, de ordenação ou definir uma manipulação enquanto transferindo elementos de um container para outro
int square(int val) { return val * val; }
int main()
{
vector<int> col;
vector<int> col2;
for (int i=1; i<=9; ++i)
col.push_back(i);
transform(col.begin(), col.end(), std::back_inserter(col2), square);
cout << endl;
}
Predicados
São funções que retornam um valor Booleano Usadas em critérios de busca ou ordenação Predicado sempre retorna o mesmo resultado para o mesmo
valor
bool isPrime (int number) { ... }
list<int> col;// ... preenche a lista
list<int>::iterator pos;pos = find_if(col.begin(),col.end(),isPrime);if (pos != col.end())
cout << “Primeiro número primo é: “ << *pos << endl;else
cout << “Nenhum número primo encontrado” << endl;
Predicados Binários
Comparam propriedades específicas de dois argumentos
class Pessoa {public:
string nome(); string sobrenome();
}bool pessoaCriterio(cont Pessoa& p1, const Pessoa& p2){ return ( p1.sobrenome() < p2.sobrenome() || (!p2.sobrenome() < p1.sobrenome()) &&
p1.nome() < p2.nome());}
deque<Pessoa> col;...sort(col.begin(), col.end(), pessoaCriterio);
Objetos Função
Objetos que comportam-se como função são functors O que é uma função? algo que pode ser chamado usando parênteses e
passando argumentos: function(arg1,arg2)
Como criar classes que podem se comportar como função:class X {
public:
valor_retorno operator() (argumentos);
...
};
X fo;
fo(arg1,arg2);
fo.operator() (arg1,arg2);
Objetos Função
class PrintInt{public:
void operator() (int elem) const { cout << elem << ‘ ‘;
}}
void main() {
vector<int> col;// ...
for_each(col.begin(), col.end(), PrintInt);}
Objetos Função
São mais “espertos” que funções pois podem possuir mais habilidades que o operador (). Podem ter outros membros e atributos, ou seja tem um estado próprio
Cada objeto função tem seu próprio tipo, mesmo que sua assinatura seja idêntica a de outro objeto
São usualmente mais rápidos que funções ordinárias
class AddVal() {public: AddVal(int v) : val_(v); void operator(int& elem) { elem += val_; }}
AddVal add3(3);AddVal add4(4);for_each(col.begin(),col.end(),add3);for_each(col.begin(),col.end(),add4);
Elementos de Containers
Um elemento de container devem ser copiável por um construtor de cópia
containers guardam cópias dos elementos nele inseridos
Um elemento de container deve ser associável por um operador de associação
Um elemento de container deve ser destruível por um destrutor
ContType c cria um container vazioContType c1(c2) copia um container do mesmo tipoContType c(beg, end) cria e inicializa com os elem. do intervalo
c.size() retorna o numero de elementos do containerc.empty() retorna se o container esta vazio ou não
c.insert(pos, elem) insere elemento em posiçãoc.erase(beg,end) remove elementos do intervaloc.clear() remove todos os elementos
Iterator Adapters
Iteradores são abstrações: qualquer coisa que se comporte
como um iterador é um iterador.
É possível escrever classes que oferecem interfaces iguais a
dos iteradores mas fazem coisas diferentes
STL fornece alguns iteradores especiais com comportamento
pré-definido: iterator adapters
Acrescentam poder ao conceito de iteradores
Insert Iterators
Algoritmos STL trabalham sobre iteradores e não sobre coleções
Iteradores não conhecem detalhes das coleções que estão
percorrendo
Iteradores tradicionais trabalham no modo sobreposição:
col2.resize(col1.size()); // necessário
copy(col1.begin(), col1.end(), col2.begin(); // pré-alocação
Insert Iterators : permitem que os iteradores trabalhem em modo
inserção . Eliminam o problema de algoritmos que tentam
escrever em destinos sem memória suficiente: destino cresce
automaticamente
Insert Iterators
Back Inserters: inserem novos elementos ao final da coleção
atrravés do método (push_back)
copy(col1.begin(),col2.end(),back_inserter(col2));
Front Inserters: inserem novos elementos no início da coleção
através do método (push_front)
copy(col1.begin(),col2.end(),front_inserter(col2));
Inserters: inserem na posição seguinte à posição passada como
segundo parâmetro na sua construção. Usam o método (insert)
copy(col1.begin(),col2.end(),inserter(col2, col2.begin()));
Stream Iterators
São iteradores que lêem de e escrevem em um objeto que representa um canal de entrada/saída (stream)
Os caracteres lidos de um teclado comportam-se como uma coleção
A saída de um algoritmo pode ser redirecionada para um arquivo ou tela
vector<string> col;
copy(istream_iterator<string>(cin), // começo da fonte
istream_iterator<string>(), // fim da fonte
back_inserter(col)); // destino
sort(col.begin(),col.end());
copy(col.begin(),col.end(), // fonte
ostream_iterator<string>(cout,”\n”); // destino
Invalidação de Iteradores
Operações de inserção e remoção podem invalidar iteradores Invalidação de iteradores depende do container
vector<int> coll;vector<int>::iterator it = coll.begin();
for (int i=0; i <4; i++){
coll.insert(it,i); // Erro! it é invalidado após inserção
it++;}
Invalidação de Iteradores
Operações de inserção e remoção podem invalidar iteradores Invalidação de iteradores depende do container
vector<int> coll;vector<int>::iterator it = coll.begin();
for (int i=0; i <4; i++){
it = coll.insert(it,i); // o.k
it++;}
Objetos Função
Permitem que os algorimos executem um código fornecido pelo usuário
for_each(col.begin(),col.end(),AddValue(10));
Predicados são funções que retornam verdadeiro ou falso
pos = find_if(col.begin(),col.end(),isPrime)
Objetos Função e Predicados pré-definidos
Predicados <funcional>
equal_to (binário) arg1 == arg2greater (binário) arg1 < arg2less (binário) arg1 > arg2...
Objetos Funcionais plus (binário) arg1 + arg2minus (binário) arg1 - arg2multiplies (binário) arg1 * arg2...
Adaptadores de função
Predicados e funções podem ser os pré-definidos ou escritos pelos usuários
Muitas vezes necessita-se de um predicado que é ligeiramente diferente de um predicado pré-definido
Para isso são usados adaptadores
pos = find_if(col.begin(),col.end(),bind2nd(greater<int>(),42))
// retorna primeiro elemento menor que 42
pos = transform(col.begin(),col.end(), col2.begin(),bind1st(minus<int>(),1000))
// copia para coleção 2, 1000 menos os elementos de coleção 1
Adaptadores para funções membro
class Person {
//...
void print () const {
std::cout << name << std::endl;
}
void printWithPrefix (std::string prefix) const {
std::cout << prefix << name << std::endl;
}
};
std::vector<Person>& coll;
//....
for_each (coll.begin(), coll.end(), mem_fun_ref(&Person::print));
for_each (coll.begin(), coll.end(),
bind2nd(mem_fun_ref(&Person::printWithPrefix),
"person: "));
Containeres
Containeres são diferentes tanto em eficiência quanto em
termos de operações e invalidação de iteradores: escolha com
cuidado
Containeres guardam cópias de elementos e retornam cópias:
forneça construtores de cópia e operadores de associação
eficientes
Use if (c.empty()) ao invés de if (c.size() == 0)
Prefira funções e algoritmos que trabalham sobre range ao
invés de manipular elemento a elemento
Exemplo
Dados dois vetores v1 e v2 qual a melhor forma de fazer com que o conteúdo de v1 seja igual a segunda metade do conteúdo de v2?
Usando loopv1.clear();vector<Widget>::const_iterator ci;for (ci=v2.begin() + v2.size()/2; ci != v2.end(); ci++){
v1.push_back(*ci);} Resposta: usar membro assign ao invés de loop nos elementos:v1.assign(v2.begin()+v2.size()/2, v2.end());
v1.clear();copy(v2.begin()+v2.size()/2,back_inserter(v1));
Algoritmos que não modificam estrutura
Servem para encontrar algo sobre a estrutura ou sobre os elementos da estrutura sem escrever loops explícitos
for_each : retorna a função passada como terceiro argumento
class MeanValue { private: long num; // número de elementos long sum; // soma de todos os valores dos elementos public:
MeanValue () : num(0), sum(0) {}void operator() (int elem) {
num++; // incrementa contador sum += elem; // acumula valor } double value ()
{ return static_cast<double>(sum) / static_cast<double>(num); }};
MeanValue mv = for_each (coll.begin(), coll.end(), MeanValue()); cout << "mean value: " << mv.value() << endl;
Família Find
Percorre uma sequência ou um par de sequências a fim de encontrar um valor ou um elemento que atenda o predicado
p = find(col.begin(),col.end(),3)p = find_if (col.begin(),col.end(),isPrime)
vector<int> coll;list<int> searchcoll;
pos = find_first_of (coll.begin(), coll.end(), searchcoll.begin(), searchcoll.end());
pos = adjacent_find (coll.begin(), coll.end());if (pos != coll.end()) { cout << "first two elements with equal value have position " << distance(coll.begin(),pos) + 1
Count
vector<int> coll; // conta o número de elementos com valor 4 num = count (coll.begin(), coll.end(), 4); cout << "number of elements equal to 4: " << num << endl;
// conta o número de elementos pares num = count_if (coll.begin(), coll.end(), isEven); cout << "number of elements with even value: " << num << endl;
// conta número de elementos com valor maior que 4 num = count_if (coll.begin(), coll.end(), bind2nd(greater<int>(),4)); cout << "number of elements greater than 4: " << num << endl;
Algoritmos que modificam estrutura
É melhor aplicar algoritmos do que iterar sobre eles:
for (it = col1.begin(); it!= col1.end(); it++)col2.push_back(*it);
copy(col1.begin(); col1.end(), col2.begin());
transform(col.begin(); col.end(); col2.begin(), square);
pos = unique (col.begin(), col.end());
swap (sentence[1], sentence[3]);
Resumo
Vector Deque List
Estrutura interna array dinâmico array de array lista duplamente linkada
Elementos valor valor valor
Valores duplicados
sim sim sim
Acesso aleatório sim sim não
Iteradores acesso aleatório acesso aleatório bidirecional
Busca elementos lenta lenta muito lenta
Inserção/Remoção é rápida
no fim no fim e no começo
qualquer lugar
Invalida iteradores
qdo realoca sempre nunca
Libera memória remoção
nunca às vezes sempre
Resumo
Set Multiset Map Multimap
Estrutura interna árvore binária árvore binária árvore binária árvore binária
Elementos valor valor (chave,valor) (chave,valor)
Valores duplicados não sim não para chave sim
Acesso aleatório não não com a chave não
Iteradores bidirecional bidirecional bidirecional bidirecional
Busca elementos rápida rápida rápido para chave
rápido para chave
Inserção/Remoção é rápida
--- --- --- ---
Invalida iteradores nunca nunca nunca nunca
Libera memória remoção
sempre sempre sempre sempre
Containeres Especiais
Stack: métodos push(), pop(), top()
#include <stack>stack<int> st;
Queues: métodos push(), front(), back(), pop()
#include <queue>queue<string> q;
Priority Queues: métodos push(), pop(), top()
#include <queue>priority_queue<float,vector<float>,greater<float> > p;
Bitsets: arrays de bits (valores booleanos)enum Color { red, green, blue ... }bitset<numColors> usedColors;usedColors.set(red);usedColors.set(green);
Strings
Podem ser usados como tipos normais
Podem ser copiadas, comparadas, associadas como tipos fundamentais sem
a preocupação sobre a existência de memória suficiente p. ex.
Operadores : = , ==, + (concatenação)#include <string>
string filename, tmpname;
string suffix(“tmp”);
int idx = filename.find(‘.’);
if (idx == string::npos)
tmpname = filename + suffix;
else
{
string basename = filename(0,idx);
tmpname = basename + suffix;
}
I/O Streams
Em C++ entrada/saída é executada através de streams: canais de entrada
e saída onde fluem sequências de caracteres. Entrada é interpretada como
dados fluindo de um stream e saída como dados fluindo para um stream
Existem duas classes de streams: istream e ostream
A biblioteca IOStream define alguns objetos globais do tipo istream e
ostream . Esses objetos correspondem aos canais padrão de entrada e
saída: cin : canal de entrada padrão, geralmente conectado, pelo sistema operacional,
ao teclado cout: canal de saída padrão, geralmente conectado, pelo sistema operacional, ao
monitor cerr: canal de saída padrão para a saída de erros, geralmente também
conectado ao monitor clog: canal de saída padrão para a saida de log, geralmente também
conectadoao monitor
I/O Streams
Operadores de deslocamento << e >> são sobrecarregados para
representarem os operadores de entrada e saída
Manipuladores: objetos especiais que manipulam os streams
endl envia um ´\n´ e descarrega o buffer
ends envia um ´\0´
flush descarrega o buffer
ws lê e descarta espaços em branco
Formatação é feita através de flags
cout << std::setw(8) << std::setfill(´_´) << -3.14 << endl;
// ___-3.14
Acesso a arquivos
Streams podem ser usadas para acessar arquivos: ofstream e ifstream. Permissões são controladas por flags
Exemplo:const string saida = “xxx.txt”;const string entrada = “yyy.txt”;ofstream file(saida.c_str(),std::ios:app);if (!file) // erro na abertura do arquivo{ ... }for (int i=32; i<256; i++)
file << i << “ “;
ifstream file2(entrada.c_str());if (file2) {
char c; while (file.get(c))
cout.put(c)}