Standard Template Library STL Claudio Esperança Paulo Roma Cavalcanti 2008 LCG/UFRJ. All rights reserved. 1 O que é a STL? • STL é uma biblioteca padrão com as estruturas de dados mais usadas em Computação. ▪ Foi criada para evitar a implementação e teste das mesmas estruturas de dados eternamente. ▪ Por isso, economiza muito tempo e evita “dor de cabeça” desnecessária. • Por que templates? ▪ Elas lhe poupam da tarefa de reimplementar o código para cada novo tipo de dado da sua aplicação. 2008 LCG/UFRJ. All rights reserved. 2 STL • Quando usadas apropriadamente, as templates são bastante eficazes. ▪ No entanto, as mensagens de erro costumam ser pouco elucidativas. • Uma boa fonte sobre a STL é o guia de programação da SGI. ▪ Ou então a nossa implementação de um subconjunto da STL. • Na nossa implementação, cada classe tem o prefixo “utl” (não havia namespace ainda). ▪ Se a variável de compilação USE_STL estiver definida, o prefixo será suprimido, “cgcUtil::” será substituído por “std::”, e o compilador usará a STL nativa então. 2008 LCG/UFRJ. All rights reserved. 3 Estrutura da STL • A STL oferece: ▪ Containers (recipientes). ▪ Iterators (iteradores). ▪ Algorithms (algoritmos). • Recipientes servem para armazenar dados e podem ser classificados como: ▪ Seqüências de dados ▪ Recipientes associativos para pares de objetos (chave, dado). ▪ Adaptadores que provêem um subconjunto da funcionalidade de um recipiente. ◦ Pilhas, filas, filas de prioridade. 2008 LCG/UFRJ. All rights reserved. 4 Iteradores • Iteradores foram criados para permitir uma maneira unificada de percorrer ou recuperar dados de recipientes. ▪ Escondem os detalhes de implementação, principalmente ponteiros, das aplicações. ▪ Portanto, geralmente é possível trocar o tipo de recipiente e ainda assim usar o mesmo código. • A STL adota a filosofia de manter os algoritmos fora das classes dos recipientes. ▪ A razão é permitir que o mesmo algoritmo possa agir sobre recipientes diferentes. 2008 LCG/UFRJ. All rights reserved. 5 Exemplo • Geralmente os algoritmos são implementados usando iteradores apenas. ▪ Ordenação, busca, contagem, substituição, etc... ▪ Iteradores tem operadores de incremento “++” definidos. ▪ Ponteiros podem ser usados como iteradores. 2008 LCG/UFRJ. All rights reserved. 6 #include <vector> #include <iostream> using namespace std; /** função objeto. * Insere T em um stream de saída. * Substitui “passar ponteiro para função” de C. */ template <class T> struct print : public unary_function <T, void> { print(ostream& out) : os(out), count(0) {} void operator() (T x) { os << x << ' '; ++count; } ostream& os; int count; }; Construtor int { Redefine operador ( ) 7 Imprimindo elementos de um vetor Calcula o Chama print() tamanho de para cada main() A Guarda elemento de A Dois int A[] = {1, 4, 2, 8, 5, 7}; número de const int N = sizeof(A) / sizeof(int);iteradores elementos print<int> P = for_each(A, A + N, print<int>(cout)); impressos cout << endl << P.count << " objects printed." << endl; return 1; } 1 4 2 8 5 7 6 objects printed. 2008 LCG/UFRJ. All rights reserved. Vector • vector é o recipiente mais simples da STL. ▪ É uma seqüência que suporta acesso aleatório aos seus elementos. ▪ Inserção e remoção de elementos no final em O(1). ▪ Inserção e remoção de elementos no meio em O(n). ▪ Busca em O(n). ▪ Gerenciamento automático de memória. ▪ Iteradores estão invalidados após realocação de memória, inserção e remoção no meio. ▪ Descrição e implementação. 2008 LCG/UFRJ. All rights reserved. 8 #include <vector> #include <iostream> #include <iterator> #include <ext/algorithm> using namespace std; using __gnu_cxx::is_sorted; Insere no final de V. int main() { vector<int> V; Insere V no V.push_back(20); Ordena V V.push_back(30); stream de V.push_back(-1); com merge saída V.push_back(-1); sort. V.push_back(-1); Verifica se cout << "V: \n"; copy(V.begin(), V.end(), ostream_iterator<int>(cout, " ")); V está stable_sort (V.begin(), V.end()); ordenado. cout << "\nV: sorted\n"; copy(V.begin(), V.end(), ostream_iterator<int>(cout, " ")); cout << "\nV: is sorted? "; cout << is_sorted ( V.begin(), V.end() ) << "\n"; } V: 20 V: -1 V: 30 -1 -1 -1 sorted -1 -1 20 30 is sorted? 1 9 Uso da classe vector. 2008 LCG/UFRJ. All rights reserved. /** copies elements from a container to another. * * Copies elements from the range [first, last) to the * range [result, result + (last - first)). * That is, it performs the assignments * *result = *first, *(result + 1) = *(first + 1), and so on. * Generally, for every integer n from 0 to last - first, * copy() performs the assignment *(result + n) = *(first + n). * Assignments are performed in forward order, i.e. in order of * increasing n. * * The return value is result + (last - first) * * @param first where to start copying. * @param last where to end copying. * @param result where to copy. * @return result + (last-first). */ template<class InputIterator, class OutputIterator> inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { for( InputIterator itr = first; itr != last; ++itr, ++result ) { *result = *itr; } return result; } Apenas percorre o recipiente de entrada. 10 Função copy. Derivados da mesma classe base. E atribui ao recipiente da saída. 2008 LCG/UFRJ. All rights reserved. Deque • deque (Double Ended Queue) é como um vetor com inserção eficiente na frente também. ▪ É uma seqüência que suporta acesso aleatório aos seus elementos. ▪ Inserção e remoção de elementos no meio em O(n). ▪ Busca em O(n). ▪ Inserção e remoção de elementos na frente e no final em O(1). ▪ Gerenciamento automático de memória. ▪ Iteradores estão invalidados após realocação de memória, inserção e remoção no meio. ▪ Descrição e implementação. 2008 LCG/UFRJ. All rights reserved. 11 #include <deque> #include <iostream> #include <iterator> 12 using namespace std; int main() { Uso da classe deque. deque<int> Q; Q.push_back(3); Insere 5 três Q.push_front(1); Q.insert(Q.begin() + 1, 2); vezes após a Q[2] = 0; primeira copy(Q.begin(), Q.end(), ostream_iterator<int>(cout, " ")); posição. cout << "\n"; // The values that are printed are 1 2 0 Q.insert(Q.begin() + 1, (size_t)3, 5); Q.push_front(7); Q.push_front(8); Q.push_front(9); 2008 LCG/UFRJ. All rights reserved. Redimensiona copy(Q.begin(), Q.end(), P completando cout << "\n"; comP 88 no final. deque<int> = Q; 13 ostream_iterator<int>(cout, " ")); copy(Q.begin(), Q.end(), ostream_iterator<int>(cout, " ")); Remove os dois cout << "\n"; primeiros P.resize ( 18, 88 ); elementos de P. P.push_front ( 99 ); P.push_front ( 98 ); copy(P.begin(), P.end(), ostream_iterator<int>(cout, " ")); P.erase ( P.begin(), P.begin()+2 ); Último cout << "\n"; elemento de P cout << P.back(); cout << "\n"; Primeiro cout << P.front(); elemento de P cout << "\n"; cout << *P.begin(); cout << "\n"; } 1 2 0 9 8 7 1 5 5 5 2 0 9 8 7 1 5 5 5 2 0 98 99 9 8 7 1 5 5 5 2 0 88 88 88 88 88 88 88 88 88 88 9 9 Uso da classe deque. 2008 LCG/UFRJ. All rights reserved. List • list é de fato uma lista duplamente encadeada. ▪ É uma seqüência que suporta percurso para frente e para trás. ▪ Busca em O(n). ▪ Inserção e remoção de elementos na frente, no meio e no final em O(1). ▪ Iteradores ainda estão válidos após inserção, splicing ou remoção. ▪ Descrição, Implementação. 2008 LCG/UFRJ. All rights reserved. 14 #include <list> #include <iostream> #include <iterator> 15 using namespace std; Insere 0 no final de L Insere 1 no int main() { início de L. Uso da classe list. list<int> L; L.push_back(0); L.push_front(1); L.insert(++L.begin(), 2); copy(L.begin(), L.end(), ostream_iterator<int>(cout, " ")); cout << "\n"; list<int> M ( L ); Insere 2 L.splice ( ++++L.begin(), M ); após a copy(L.begin(), L.end(), ostream_iterator<int>(cout, " ")); cout << “\n”; primeira copy(M.begin(), M.end(), ostream_iterator<int>(cout, " ")); posição. } Move elementos de M para L. 1 2 0 1 2 1 2 0 0 M está vazia. 2008 LCG/UFRJ. All rights reserved. String • string toma o lugar de um “array of char”. ▪ Não é mais necessário se preocupar com o tamanho do arranjo. ▪ Oferece todas as funções de C pertinentes a strings como membros da classe. ▪ Evita erros relacionados com ponteiros, na chamada de funções que recebem ou retornam strings. ▪ Descrição, Implementação. 2008 LCG/UFRJ. All rights reserved. 16 #include <string> #include <iostream> using namespace std; int main () { //test constructors string str0; cout << "empty constructor: " << str0 << endl; cout << "str0 size = " << str0.size() << endl; mesmo que world!"); string O str1("hellow Comprimento Capacidade da // 0123456789012 length. Tamanho cout << atual "const constructor: string da char string sem termáximo" << str1 << endl; cout << "data de = "realocar. << str1.data() que uma string << endl; cout << "size = " << pode str1.size() << endl; ter. cout << "length = " << str1.length() << endl; cout << "capacity = " << str1.capacity() << endl; cout << "max_size = " << str1.max_size() << endl; // empty cout << "str0 empty = " << str0.empty() << endl; cout << "str1 empty = " << str1.empty() << endl; empty constructor: str0 size = 0 const char constructor: hellow world! data = hellow world! size = 13 length = 13 capacity = 13 max_size = 13 str0 empty = 1 str1 empty = 0 17 Uso da classe string. 2008 LCG/UFRJ. All rights reserved. string str2(str1); cout << “copy constructor: " << str2 << endl; string str3(str1, 4, 6); cout << "string&, pos, npos, str3 constructor: " << str3 << endl; string str4("hellow word!", 6); Troca o npos, str4 constructor: " << str4 << endl; cout << "char[], string str5(12, conteúdo 'h'); de cout << “n, char str5 constructor: " << str5 << endl; str5 por str1. cout << "str5 size = " << str5.size() << endl; // swap str5.swap(str1); cout << "swap str1 and str5" << endl; cout << "str1 = " << str1 << endl; cout << "str5 = " << str5 << endl; } copy constructor: hellow world! string&, pos, npos, str3 constructor: ow wor char[], npos, str4 constructor: hellow n, char str5 constructor: hhhhhhhhhhhh str5 size = 12 swap str1 and str5 str1 = hhhhhhhhhhhh str5 = hellow world! 18 Uso da classe string. 2008 LCG/UFRJ. All rights reserved. Set • set é uma coleção ordenada de objetos do tipo “key” sem duplicatas. ▪ Operações de conjunto como interseção, união e diferença são eficientes. ▪ Implementado por uma árvore balanceada de busca (Red Black, Splay). ▪ Busca em O(log n). ▪ Descrição, Implementação. • multiset é a versão que permite duplicatas. ▪ Descrição, Implementação. 2008 LCG/UFRJ. All rights reserved. 19 #include <set> #include <iostream> using namespace std; 20 Retorna se s1 é lexicograficamente /// função objeto. Compara duas struct ltstr menor que s2 { seqüências de caracteres. bool operator()(const char* s1, const char* s2) const { return strcmp(s1, s2) < 0; } }; Uso da classe set. int main() { const int N = 6; const char* a[N] = {"isomer", "ephemeral", "prosaic", "nugatory", "artichoke", "serif"}; const char* b[N] = {"flat", "this", "artichoke", "frigate", "prosaic", "isomer"}; Construtor a partir de dois iteradores set<const char*,ltstr> A(a, a + N); set<const char*,ltstr> B(b, b + N); set<const char*,ltstr> C; 2008 LCG/UFRJ. All rights reserved. cout << "Set A: copy(A.begin(), cout << endl; cout << "Set B: copy(B.begin(), cout << endl; A ∪ B. "; A.end(), ostream_iterator <const char*>(cout, " ")); 21 "; B.end(), ostream_iterator <const char*>(cout, " ")); cout << "Union: "; set_union(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<const char*>(cout, " "), ltstr()); cout << endl; Uso da classe set. A ∩ B. cout << "Intersection: "; set_intersection(A.begin(), A.end(), B.begin(), B.end(), ostream_iterator<const char*>(cout, " "), ltstr()); cout << endl; A – B. set_difference(A.begin(), A.end(), B.begin(), B.end(), inserter(C, C.begin()), ltstr()); cout << "Set C (difference of A and B): "; copy(C.begin(), C.end(), ostream_iterator <const char*>(cout, " ")); cout << endl; } Insere o resultado em C. Set A: artichoke ephemeral isomer nugatory prosaic serif Set B: artichoke flat frigate isomer prosaic this Union: artichoke ephemeral flat frigate isomer nugatory prosaic serif this Intersection: artichoke isomer prosaic Set C (difference of A and B): ephemeral nugatory serif 2008 LCG/UFRJ. All rights reserved. Map • map associa objetos do tipo “key” a objetos do tipo “data”. ▪ Nenhum par de elementos possui a mesma chave. ▪ Percurso é ordenado. ▪ Indicada para implementação de dicionários. ▪ Implementada por uma árvore balanceada de busca (Red Black, Splay). ▪ Busca em O(log n). ▪ Descrição, Implementação. • multimap é a versão que permite duplicatas. ▪ Descrição, Implementação. 2008 LCG/UFRJ. All rights reserved. 22 #include <map> #include <string> #include <iostream> 23 using namespace std; Chave e dado. typedef map < long, string > mapType; typedef mapType::value_type ValuePair; int main() Duas formas de inserção { Uso da classe map. mapType Map; Map[836361136] = "Andrew"; Map[274635328] = "Berni"; Map[974635328] = "Ulisses"; Map[277735328] = "Paulo"; Map[277825328] = "Pedro"; Map[266735328] = "Peter"; Map[275734328] = "Paula"; Map[275839328] = "Paulos"; Map.insert(ValuePair(260736622, "John")); Map.insert(ValuePair(720002287, "Karen")); Map.insert(ValuePair(138373498, "Thomas")); Map.insert(ValuePair(135353630, "William")); // insertion of Xaviera is not executed, because // the key already exists. Map.insert(ValuePair(720002287, "Xaviera")); Cria um par (chave,dado). mapType Map2 ( Map.begin(), Map.end() ); cout << "equality operator " << (Map2 == Map) << endl; equality operator 1 Output: 974635328:Ulisses 836361136:Andrew 720002287:Karen 277825328:Pedro 277735328:Paulo 275839328:Paulos 275734328:Paula Ordem decrescente. 2008 LCG/UFRJ. All rights reserved. cout << "Output:\n"; Iterador 720002287 reverso. ); Map.erase ( Map2.swap ( Map ); Cast para iterador constante. mapType::const_reverse_iterator iter = Map.rbegin(); while( iter != (mapType::const_reverse_iterator)Map.rend() ) { cout << (*iter).first << ':' << (*iter).second << endl; ++iter; } 24 Uso da classe map. cout << "Output of the name after entering the number\n" << "Number: "; long Number; Se encontrado. >> Number; Busca é O(log n). cin mapType::const_iterator it = Map.find(Number); if(it != Map.end()) { cout << (*it).second << ' ' // O(1) << endl; } else cout << "Not found!" << endl; } 274635328:Berni 266735328:Peter 260736622:John 138373498:Thomas 135353630:William Output of the name after entering the number Number: 275734328 Paula 2008 LCG/UFRJ. All rights reserved. Hash Map • hash_map associa objetos do tipo “key” a objetos do tipo “data”. ▪ Nenhum par de elementos possui a mesma chave. ▪ Não há ordem de percurso. ▪ Implementada por uma hash table. ◦ Há uma função de hash que gera endereços a partir de chaves. ▪ Busca em O(1). ▪ Descrição, Implementação. • hash_multimap é a versão que permite duplicatas. ▪ Descrição, Implementação. 2008 LCG/UFRJ. All rights reserved. 25 #include <ext/hash_map> #include <string> #include <iostream> 26 using namespace std; using __gnu_cxx::hash_map; using __gun_cxx::hash; Recebe string e // define your own hash function template <class T> class my_hash: public hash<T> { passa char*. public: // initial hash table size #define table_size 11 Uso da classe hash_map. my_hash() {} size_t operator()(const string& p) const { hash<const char*> h; return h(p.c_str()); } }; typedef hash_map<string, string, my_hash<string> > mapType; typedef mapType::value_type ValuePair; int main() { mapType Map ( table_size, my_hash <string>() ); Map["ANDREW"]="Andrew"; Map["BERNI"]="Berni"; Map["JOHN"]="John"; Map.insert(ValuePair("KAREN", "Karen")); Map.insert(ValuePair("THOMAS", "Thomas")); Map["WILLIAM"]="William"; // insertion of Xaviera is not executed, because // the key already exists. Map.insert(ValuePair("BERNI", "Xaviera")); 2008 LCG/UFRJ. All rights reserved. cout << "Output:\n"; 27 mapType::iterator iter = Map.begin(); while(iter != Map.end()) { cout << (*iter).first << ':' << (*iter).second << endl; ++iter; } Elimina \n do final de name after entering buf. cout << "Output of the the key\n" << "Key: "; char buf[256]; buf[0] = '\0'; fgets ( buf, 256, stdin ); if ( buf[strlen(buf)-1]=='\n' ) buf[strlen(buf)-1] = '\0'; string key ( buf ); iter = Map.find(key); if(iter != Map.end()) { cout << (*iter).second << ' ' << Map[key] << endl; } else cout << "Not found!" << endl; Uso da classe hash_map. Busca é O(1) } Output: JOHN:John BERNI:Berni KAREN:Karen ANDREW:Andrew THOMAS:Thomas WILLIAN:Willian Output of the name after entering the key Key: KAREN Karen Karen Não há ordem. 2008 LCG/UFRJ. All rights reserved.