You are on page 1of 49

CAP.

2 OPTIMIZAO DE DESEMPENHO

2.1. Introduo
A sobrecarga de operadores em classes com alojamento dinmico (String, Bigint) que possam atingir grandes dimenses, requer a adopo de tcnicas tendentes a evitar graves penalizaes de desempenho. Os problemas so o aumento significativo de instanciaes de objectos temporrios e locais. Isto porque algumas funes de sobrecarga de operadores implicam o retorno, por valor, de objectos locais. O objectivo deste captulo descrever tcnicas para minimizar o nmero de instanciaes e cpias, de modo a no desmerecer em desempenho face ao paradigma procedimental. As classes de inteiros e fraces com mltipla preciso so pretexto para introduzir algoritmos numricos e conceitos bsicos que do suporte a aplicaes no domnio da segurana das comunicaes.

2.2. Classe String


entendida como uma classe contentora de caracteres, que providencia um n alargado de mtodos, como concatenao (+), append (+=), afectao (=), extraco e insero sobre streams ( >> e << ), alm de pesquisa, substituio, insero e remoo de sub-strings, decomposio em palavras, etc. Uma estrutura string C-style consiste simplesmente numa sequncia de caracteres terminada pelo carcter \O (carcter terminal). A classe String que a seguir se apresenta utiliza uma string C-style para a representao dos caracteres que contm. 2.2.1. Verso Bsica Interface Pblica: Construtores: String ( ) Construtor de string vazia String ( String&) Construtor por cpia String (char*) Construtor a partir de uma string C-style Destrutor: ~String ( ) Devolve ao heap o espao nele reservado Mtodos de acesso: int size ( ) Retorna o n corrente de caracteres int capacity ( ) const char *c_str( ) Retorna a string C-style Operadores de afectao: String &operator= (const String&) Afectao por cpia String &operator= (const char*) Afectao com string C-style String &operator+= (const String&) append Mtodos de acesso a caracteres: char readAt (int index) const void writeAt(int index, char c) Operadores globais String operator+ (const String&, const String&) cannicos so o construtor por cpia, operador afectao e o destrutor. Cada objecto da classe String ter como atributos um apontador para a representao, a dimenso da sequncia de caracteres e a dimenso da memria alojada. A construo de um objecto com alojamento dinmico da representao deve incluir os passos: - Reserva de espao na memria livre (heap) - Afectao do respectivo atributo apontador com o endereo da estrutura - Cpia para o espao reservado, dos dados a inserir A afectao por cpia, envolve: - Reservar, caso necessrio, um novo espao onde caiba a nova representao - Copiar - Devolver o espao

- Afectar o respectivo atributo apontador com o endereo do novo espao. O destrutor deve providenciar a devoluo da memria ocupada. String forma cannica --> pg. 60 Justifica-se a definio de mtodos auxiliares, sempre que venham a ser invocados mais que uma vez na definio de outros mtodos. o caso do init ( ), do assign ( ) e do dimNormalize ( ). Na reserva de espao adoptamos dimenses mltiplas de DIM_MIN (por potncias de 2), excedentes a sz. O operador concatenao definido como funo global, dado tratar-se de um operador simtrico entre uma string e uma string C-style. No necessita ser declarado como friend da classe String pois no precisa aceder a atributos privados dessa classe. Foi definido a partir do +=, o que um estilo muito recomendvel. O operador += devolve uma referncia para o objecto alvo, pelo que no penalizante para o tempo de execuo. O que se torna gravoso na concatenao a necessidade de retornar por valor o objecto String resultante e a construo do objecto String tmp, local funo. Veremos como melhorar. O uso do tipo size_t recomendvel para a portabilidade 2.2.2. Verso Handle/Body O desempenho da verso cannica anterior pode ser melhorado, evitando as mltiplas cpias da representao string C-style aquando da execuo do construtor por cpia e do operador afectao por cpia. Isso consegue-se usando o idioma handle/body, que consiste em criar 2 classes copperantes: - Uma classe String (handle), nica vsivel ao utilizador - Uma classe Srep (body) que se comporta como intermediria entre a classe String e a representao string C-style. A classe String tem como nico atributo um apontador para Srep e apresenta uma interface idntica cannica. A classe Srep toma os atributos que na verso anterior pertenciam classe String e alm deles um contador de referncias representao string C-style, que memoriza o nmero de objectos String que em cada momento partilham dessa representao. s mtodos so do encargo de Srep. A cada representao de String fica univocamente associado um objecto Srep. Instanciae spor cpia ou afectaes de objectos String com outros j existentes tero como consequncia que uma mesma cadeia de caracteres ficar partilhada por vrios objectos String, evitando assim mltiplas cpias de cadeias de caracteres. O custo disto a ateno necessria quando uma alterao num objecto String poder vir a repercutir-se nos outros objectos que com ela partilham essa cadeia; ter pois de desligar-s epreviamente. 2.2.2.1. Definio da classe String (handle/body) ---> ver pg. 66 O tempo de desempenho metade do cannico e menor que com a classe string da biblioteca do C++. 2.2.3. Optimizao Quanto a Objectos Temporrios O idioma handle/body tem impacto nas aplicaes em que os objectos so construdos ou afectados por cpia frequentemente mas no so depois alterados. Nos casos como concatenao em strings ou operadores aritmticos em classes numricas o idioma pouco beneficia pois os objectos so muitas vezes alterados. Ento, seguindo o nosso ex. vai-se definir uma nova classe StrTmp (string temporria) e providenciar que em todas as circunstncias em que a sobrecarga de operadores envolva a instanciao de objectos temporrios, o objecto retornado por esses operadores seja dessa classe. No esquecer que a principal causa de perda de desempenho em classes de grande porte com alojamento dinmico reside nos operadores que retornam objectos por valor, criando localmente buffers de resultados. Por exemplo a concatenao de mais que 2 objectos string implica a criao de vrios temporrios, todos diferentes: s=s1+s2+s3+s4 envolve as seguintes aces: String tmp1 (operator+(s1,s2));

String tmp2 (operator+ (tmp1,s3); String tmp3 (operator+)tmp2,s4); s.operator=(tmp3); O ideal seria distinguir o comportamento dos objectos temporrios dos objectos String comuns, criando uma classe auxiliar StrTmp, tal que, se tmp1 tivesse espao para conter a concatenao global dos quatro objectos String, no seja necessrio criar mltiplas representaes para as concatenaes intermdias. Ficaramos com: StrTmp tmp (opearot+(s1,s2); tmp.operator+(s3); tmp.operator+(s4); s.operator=(String (tmp)); Ver alteraes na pg. 74 --< tempo de execuo reduzido de 4 para 1. 2.2.4. Operadores de ndice para Leitura A classe String disponibiliza normalmente a sobrecarga do operador ndice, que permite o acesso ao carcter indexado, para leitura ou para escrita. -Modo trivial na verso cannica: char &String::operator[] (inti) { assert(i<size()); return rep[i]; Permite que os objectos string tanto possam ficar do lado esquerdo como do direito de uma afectao, s que do lado esq. seria catastrfico (escrita) pois com o uso do idioma handle/body, h que desligar-se e alterar a representao. Assim requer-se que o operador indexao execute readAt() quando invoacdo do lado direito e writeAt()quando invocado do lado esquerdo. O mtodo de Colpien a doptar diz que: A ideia bsica consiste em definir uma classe auxiliar CRef (referncia inteligente para carcter) com sobrecarga do operador afectao com carcter e um operador coero para carcter. Em vez de se definir a sobrecarga do operador ndice retornando uma referncia para char, define-se essa sobrecarga retornando um objecto de uma classe auxiliar CRef que s e comporte como uma referncia inteligente para o carcter a aceder. Conforme o operador ndice seja evocado esquerda ou direita de um operador afectao, assim ser posto em execuo o operador afectao de CRef ou o operador coero para char de Cref. String s(trolaro); char c; //s[i] um objecto do tipo CRef //Leitura: operador coero de CRef para char c=s[2]; //c=s.operator[](2).operator char() //Escrita: operador afectao com char de CRef s[4] = c; //s.operator[](4).operator=(c); Ver implementao na pgina 76 2.2.5. Classe String com Apontador Inteligente ... 2.4. Classe Fraction (pg. 116) #include <iostream.h> classe Fraction { int num; //Numerador (positivo ou negativo). int den; //Denominador (sempre positivo). //Simplificao da fraco com o MDC (num,den). void norm( ) public: Fraction (int a) : num(a), den (1) {} Fraction (const int &a) : num(a), den(1) {} Fraction (const int &a, const int &b) : num(a), den(b) {norm( );} const int &getNum( ) const {return num;} const int &getDen( ) const {return den;} Fraction &operator+=(const Fraction &a);

Fraction &operator-=(const Fraction &b); Fraction &operator*=(const Fraction &b); Fraction &operator/=(const Fraction &a); Fraction operator-( ) const {Fraction x =+this; x.num.neg( ); return x; } #define oper(op) \ Fraction operator op(const Fraction &b) const \ {return Fraction (*this)op##&b; } oper(+) oper(-) oper(*) oper(/) undef oper friend ostream &operator<<(ostream &out, const Fraction &f) {return out << f.num << / << f.den; } }; void Fraction::norm () { if (num.isZero()) {den=1; return;} if (num.isOne() || den.isOne()) return; if (num == den) {num=den=1; eturn; } if (den < 0L) {num.neg(); den.neg(); } //algoritmo de Euclides int n=num, d=den, MDC; while (!d.isZero()) {MDC = n%d; n=d; d=MDC;} MDC=n; //Fim do algoritmo de euclides if (MDC.isOne()) return; num/= MDC; den/=MDC; } Fraction &Fraction::operator+=(const Fraction &b) { if (den==b.den) num+=b.num; else { num = num * b.den + den * b.num; den *= b.den; } norm ( ); return * this; } Fraction &Fraction::operator-=(const Fraction &b) { if (den==b.den) num -= b.num; else { num = num * b.den den * b.num; den *= b.den; } norm(); return *this; } Fraction &Fraction::operator/=(const Fraction &b) { num *= b.den; den*=b.num; norm(); return *this; } Fraction &Fraction::operator*= (const Fraction &b) { num*=b.num; den*=b.den; norm(); return *this; }

CAP. 3 TEMPLATES E DERIVAO 3.2. Templates de Classes e de Funes Um template de fine uma famlia de classes ou de funes, que tomam como parmetros type template parameters (parmetros-tipo) e non type template parameters (parmetros-valor). Das tcnicas fundamentais ligadas a templates realam-se as seguintes: - Mecanismos bsicos para definir e usar template de classes; - Template de funes e deduo de argumentos; - Template de membros; - Parmetros template que so templates de classes. 3.2.1. Templates de Classes Os de uso mais frequente so as classes contentoras. Designam-se por contentores os objectos destinados a conter outros objectos de qualquer tipo e que disponibilizam ao utilizador mtodos cmodos de acesso (inserir, remover, pesquisar, etc.) aos objectos contidos. Ex: Stack: 3.2.1.1. Classe Stack class Stack { public: typedef char T; //O tipo de cada elemento static const unsigned DIM = 32; //Dimenso do stack private: T data[Dim]; //Contentor esttico que suporta o stack unsigned size; //nmero de objectos contidos public: Stack() {size=0);} bool empty() const {return !size;} //Retorna a cpia do objecto situado no topo do stack. T &top() {return data[size-1];} //Insere objecto no topo do stack void push(const T &p) {assert(DIM!=size); data[size++]=p;} //Remove o objecto do topo do stack void pop() {assert(size>0); --size;} }; Esta definio vlida, seja qual for o tipo de objectos que se defina como T e seja qual for o valor inteiro da constante DIM. Mas tem de se alterar as primeiras linhas conforme o caso. possvel alargar esta definio de Stack tornando-a parametrizvel no tipo T e no valor DIM. 3.2.1.2. Classe Parametrizvel Stack O termo template de classes sinnimo de classe parametrizvel ou classe genrica. Um template de classes uma especificao para criar uma famlia de classes aparentadas. ex: template <typename T, unsignedDIM> //em vez de typename tambm pode ser class class Stack { T data[DIM]; unsigned size; public: Stack () {size=0; } int empty () const {return !size; } T &top () {return data [size-1]; } void push (const T &p) {assert (DIM!=size); data[size++]=p; } void pop () {assert(size>0); --size; } }; Ex. de declarao de objectos: Stack<char,32> stk1; Stack<int,100> stk2;

O cdigo dos mtodos da generalidade das classes de contentores tem a particularidade interessante de ser independente do tipo dos objectos neles contidos (como o exemplo reala), o que propicia definir um template de classes tendo como parmetros, entre outros, o tipo dos objectos a conter (parmetro-tipo) e eventualmente, como no caso presente, a dimenso inicial do contentor (parmetro-valor). Uma classe gerada (instanciada) a partir de um template de classes chamada classe template. Um template de classes pode ser definido com um nmero qualquer de parmetros-tipo e de parmetros-valor. ex: template <typename T, class W, int XX, char YY> class Xpto; typedef Xpto<int, String, 3, a> MyXpto; MyXpto ob1, ob2, ob3; - Ex. definio de um novo nome para a classe Stack<char, 20> typedef Stack<char, 20> CharStack; Stack definido antes no tem natureza dinmica. Geralmente tem sendo suportada por contentores sequenciais. 3.2.2. Templates de Funes Define uma famlia de funes. Ex: template<class T> void swap(T &a, T &b) {T aux=a; a=b; b=aux;} importante notar desde j que enquanto na instanciao de um objecto de uma classe template o tipo do objecto deve sempre explicitar o valor dos seus argumentos template, no caso da chamada a uma funo template prescinde-se geralmente dessa explicitao. Ex. para funo anterior: int x=10, y=20; swap(x,y); //compilador infere, mas tambm podia swap<int>(x,y); 3.2.2.1. Membros Funo de Templates de Classes Um membro funo de um template de classes implicitamente um template de funes membro. Toma como seus, os parmetros do template de classes a que pertence. Ex: template <class T> class Array { T *v; //Apontador para a representao unsigned dim; //Capacidade da representao unsigned size; //Nmero de elementos contidos Array (const Array&); //Impossibilitar a construo por cpia void oerator=(const Array&) //Impossibilitar afectao public: explicit Array(unsigned d); //Iniciado com dim=d ~Array () {delete [] v; } T &operator[](unsigned n) {assert (n<dim); return v[n]; } const T &operator[] (unsigned n) const {assert(n<dim); return v[n]; } unsigned size() const {return sz; } void operator << (const T &e) //Inserir no fim do Array. {assert(size() < dim); v[sz++] = e;} }; //Declarao de dois templates de funes Template <class t> void sort(Array<T>&; Template <class T> ostream &operator<<(ostream&, const Array<T>&); Quando um mebro de um template de classes definido fora da sua classe, deve ser explicitamente declarado como template. Ex: template <class T> Array<T>::Array(unsigned d) {v=new t [dim = d]; sz=0, } O template de funes globais define-se com sintaxe semelhante ao template de funes membro: ex. com bubble-sort

template <class T> void sort(Array<T> &a) {

for(unsigned i=a.size()-1; i<0; --i) for unsigned j=1; j<=i; ++j= if (a[j] < a[j-1] swap (a[j], a[j-1]); } 3.2.3. Templates com Parmetros por Omisso tal como acontece com os parmetros comuns das funes, os parmetros dos templates de classes podem ser declarados com valor por omisso. Ex: template<class t, class A = allocator<T>> List {/*...+/} Isto significa que se podem instanciar classes template List, com um ou os dois argumentos template: List<int> list1 List>double, AllocPool<double>> list2 Quando declaramos todos os parmetros por omisso. a lista de argumentos da classe template pode ser vazia. Ex: template<class T = char> class Stack {/*...+/}; Stack<> *p; //OK, apontador para Stack<char> Stack *q //Erro: Imprescindvel <> No permitido usdar parmetros por omisso em templates de funes. 3.2.4. Parmetros Template que so Templates template<class T> class List {/* ... */ }; template<class K, class V, template<class> class C = List> class map { struct pair { C<K> key; C<V> value; }; //... }; Map trata-se de um template de tabelas associativas, cujos elementos Pair tm 2 membros (key e value). Os membros key e value so contentores do tipo C, de objectos tipo K e tipo V. Deste template podem ser instanciados objectos tabelas asociativas to varaiadas como sejam: Map<int, double> //Chaves do tipo List<int> e dados do tipo List<double> Map<char, int, Stack> //Chaves do tipo Stack<char> e dados do tipo Stack<int> Para usar um parmetro template que template, necessrio especificar os parmetros que ele prprio requer. Os parmetros template que so template so sempre templates de classes. 3.2.5. Palavra-Chave typename utilizada no corpo da declarao dos templates e tem por objectivo desfazer ambiguidades em expresses nas quais o compilador no tenha possibilidade de inferir se um dado nome de um membro de um argumento template, corresponde ou no a um tipo. typename especifica corresponde de facto a um tipo. A biblioteca standard define em todos os templates de contentores um conjunto de nomes de tipos standard. Por ex: template<class T, class A = allocator<T>> class vector { public: typedef T value_type; //Tipo de elemento typedef A allocator_type; /Tipo de gestor de memria typedef typename A::size_type size_type; typedef typename A::difference_type difference_type; typedef typename A::pointer iterator; //... };

3.2.6. Templates de Membros Podemos declarar um template de mebros dentro de uma classe ou de um template de classes. Ex: template<class T< class X { public: //Template de funes membro template<class T2> int compare(const T2&) {/*...*/} // Template de estruturas nested template<class T2> struct rebind {typedef X<T2> other; }; //... }; Tambm pode ser definido fora da classe: template<class T> template<class T2> int X<T>::compare(const T2 &s) {... se dentro tiver: template<class T2> int compare(const T2&); 3.2.7. Especializao de Templates Podemos querer que uma dada classe template tenha comportamento diferente quando tiver um tipo de argumento especfico. Seja por ex. um template vector: template<class T> class vector {/*...*/} no qual pretendemos que a classe template vector<bool> tenha um comportamento diferenciado para economia de espao de representao. Ento temos de, alm de definir o template primrio, explicitar tambm a definio especializada (sempre depois): template<> vector<bool> {/*...*/} No caso dos templates de funes... 3.2.8. Exemplos de Aplicao dos Templates Ao longo do livro 3.2.9. Tcnicas (de programao muito interessantes) que Envolvem o Uso de Templates - Templates traits destinados especificao de tipos - Execuo de algoritmos em tempo de compilao 3.2.9.1. TRAITS uma famlia de templates de classes, destinada a especializar a definio de tipos noutras classes ou funes template. Para tal o template que usa traits adopta os tipos nele definidos, tirando partido das suas especializaes. Seja, por exemplo, o template UserTraits que define, nested e public, um nome de tipo: template<class T> struct UserTraits { typedef T UserType; }; Impondo especializaes ao template UserTraits, podemos, para tipos especficos de argumentos com que sejam invocadas instncias deste template, modificar a definio de UserType: template<> struct UsertTraits<int> 7 typedef float UserType; }; template<> struct UserTraits<char> { typedef int UserType; }; Seja o template de funes associado: template<class T> typename UserTarits<T>::UserType f(T t) {...} Quando em main() forem invocadas funes template f() com diversos tipos de argumentos, o retorno dessas funes ser do tipo imposto pelo UserTraits e pelas suas especializaes. ex: float x=f(10); int y=f(a); double dd= f(4.27);

3.2.9.2. Algoritmos Executados em Tempo de Compilao ... 3.3. DERIVAO DE CLASSES E MTODOS VIRTUAIS O paradigma de programao Abstract Data Type (tipos de dados definidos pelo utilizador) esgota-se com a definio de classes de objectos isoladas entre si ou, quando muito, de classes que se associam com outras atravs de relaes, tais como incluso ou agregao. O paradigma da Programao Orientada por Objectos pressupes tambm que as classes tm relaes de herana e usam polimorfismo, conceitos suportados pelos mecanismos de derivao e de mtodos virtuais do C++ 3.3.1. Relao de Herana

Numa dada aplicao complexa o programador deve encontrar classes com propriedades comuns, de modo a que a definio de algumas delas possa ser feita a partir de outras mais genricas. herdeira chama-se derivada, e herda membros da base. A derivada acrescenta normalmente novos membros dados (atributos) ou membros funo (mtodos), pelo que mais rica (em interface e atributos). Para alm de acrescentar tambm pode alterar membros. A herana pode ser simples ou mltipla (de classes base directas). Quanto acessibilidade que os membros herdados tomam numa classe derivada, depende do tipo de derivao que for adoptado: pblica; protegida; privada. O mais comum a pblica, em que os membros especificados como pblicos na classe na classe base mantm-se pblicos nas classes derivadas e os privados na base passam a ocultos, preservando assim o encapsulamento de membros. Os objectos da classe derivada incluem um objecto da classe base, mas o inverso j no verdade. assim, existe converso implcita da classe derivada para a classe base mas no o inverso. Ex. Classe B derivada de classe A //Classe base class A { int x; //atributo privado, oculto na classe B. public: void set10() {x=10;} void show() {cout << x;} }; class B: public A { //Classe derivada de A char c; //Atributo acrescentado public: //void setA10() {c=A; x=10;} //Erro. x oculto. void setA10() {c=A; set10();} //Mtodo acrescentado void show() {cout << c; A::show();} //Mtodo alterado }; void main () { A a; B b; a.set10(); cout << Show A ; a.show(); cout << endl; b.setA10(); cout << Show B ; b.show(); cout << endl; a = b; //Ok um B tambm um A b = a; //erro: A no um B. } 3.3.2. Arborescncia de Classes Derivadas (Pblicas)

A classe derivada mais especfica do que a sua classe base, ou seja, representa um subconjunto dos objectos que a sua classe base representa. atributo = atributo de estado mtodo = atributo de comportamento Os mtodos pblicos de uma classe (prprios ou herdados) chamam-se interface da classe. Uma consequncia importante que advm da derivao de classes consiste na reutilizao de cdigo da classe bsica. Derivao Pblica uma relao Is A ( Um). 3.3.3. Header da Declarao de Classes Derivadas
Por forma a suportar a herana, a sintaxe de classe permite adicionar ao header da classe uma lista de derivao dessa classe, que especifica a classe (ou classes) base directa. Na declarao de uma classe derivada, a lista de derivao segue-se ao nome da classe, antecedida por 2 pontos. EX: class D : public B1, private B2 { 3.3.3.1. Construtores de Classes Derivadas Os construtores e os operadores de afectao das classes base no so herdados pela classe derivada. Aps a lista de argumentos do construtor da classe derivada e antes do corpo do construtor, antecedida por 2 pontos e separados por vrgulas, segue-se a lista de iniciao, de que constam os construtores das classes base e os construtores dos objectos membros da classe derivada. Se isso no for feito pressupe-se que sejam invocados os seus construtores sem parmetros, caso existam. importante notar que estes s existem por omisso, se no for definido nenhum construtor com parmetros (excepo ao construtor por cpia), o construtor implicitamente invocado limita-se a reservar espao de memria, sem iniciao de valores. Da lista de iniciao (das classes base e membros) pode tambm constar a iniciao dos membros dados tipo-bsico usando para sua iniciao o formalismo da iniciao dos membros tipo-classe. ex: class Complex 7 int x, y; public: Complex(int xx=10, int yy=10) {x=xx; y=yy; } //ou Complex(intxx=10, int yy=10) : x(xx), y(yy) {} //... }; class B { Complex zz; int a; public: B(Complex v1, int v2) : zz(v1), a(v2) {} B() 7a=0} //zz iniciado com x=y=10. //... }; class D : public B { int d; public: D(Complex v3, int v4, int v5) : B(v3, v4), d(v5) {} D() {d=5;} //zz iniciado com x=y=10 e a com 0. //... }; As aces realizadas por um construtor de uma classe so as seguintes:

1 - Se for uma classe derivada, pe em execuo o(s= construtor(es) da(s) classe(s) base directas, pela ordem indicada na lista de derivao 2 - Se a classe contm atributos, pe em execuo os seus construtores pela ordem da sua declarao na classe 3 executa as instrues que constam do seu corpo. 3.3.4. Especificadores de Acesso a Membros Por forma a preservar o encapsulamento dos membros especificados como private, tambm no so acessveis aos mtodos das suas classe derivadas. Os especificados como protected mantm-se inacessveis s entidades externas mas so acessveis directamente pelos mtodos das classes derivadas. Ex: class B { int aa; //Zona apenas acessvel pelos mtodos de B protected: //Zona acessvel aos derivados de B int bb; public: //Zona pblica int cc; }; class D : public B { public: int dd; void f() { aa=0; //Erro. membro aa privado da classe B. bb=1; //OK cc=2; //oK } }; void main () { B b; D d; b.aa=0; b.bb=1; b.cc=2; b.dd=3; d.aa=4; d.bb=5; d.cc=6 d.dd=7; }

//Erro. aa privado de B (no pode ser acedido de main) //Erro. bb protegido de B //OK. cc pblico de B //Erro, dd no membro de B. //Erro. aa no acessvel a D //Erro. bb mantm-se protegido em D //OK. cc mantm-se pblico em D //Ok. dd pblico em D

3.3.5. Especificadores de Acesso s Classes Base A acessibilidade que um membro herdado toma na classe derivada depende conjuntamente do especificador de acesso classe base e do tipo de acesso que esse membro possua na sua classe. Derivao Pblica: pblicos --> pblicos; protegidos --> protegidos; privados e ocultos --> ocultos Derivao Protegida: pblicos e protegidos --> protegidos; privados e ocultos --> ocultos Derivao Privada: pblicos e protegidos --> privados; privados e ocultos --> ocultos O membro oculto, no entanto, existe na classe e poder ser acedido indirectamente por mtodos pblicos ou protegidos herdados da classe base. Se for omitido, a derivao, por omisso privada. Ex: Acesso a membros da classe base class B { int n; public:

B(in nn) {n=nn; } int getn() {return n; } void display() {cout << n= << n << endl; } }; class D : public B { char c; public: D(int nn, in cc) : B(nn), c(cc) {} //int get1() {return n+c; } //Erro. n inacessvel em D int getn() {return B::getn() +c; } //OK. invoca getn() de B //Oculta (override) B::display(). void display() {cout <<n= << getn() <<, c= << c << endl; } }; void f() { B b(7); D d(5,4); cout << b.n; //Erro. n privado a B cout << d.B::getn(); //OK. n acedido por B atravs do seu mtodo getn() cout << d.getn(); //OK b.display(); //OK d.display(); //OK B *ptB=&d; //Coero implcita de apontadores ptB --> display(); //Invoca display() de B } Os membros pblicos herdados da classe base podem ser acedidos por qualquer membro funo no esttico da classe derivada e por qualquer funo no membro, amiga ou no amiga. Os membros protegidos herdados podem ser acedidos directamente pelas funes membro da classe derivada e pelas funes declaradas amigas dessa classe. A derivao pblica promove simultaneamente derivao de interface e de implementao. raramente se justifica a derivao privada ou protegida, pelo que tudo o que segue se refere derivao pblica. 3.3.6. Sobrecarga e Redefinio de Mtodos Em classe derivadas, a sobrecarga de mtodos segue as mesmas regras estabelecidas para a sobrecarga de funes globais, desde que se tome em considerao que o alcance (scope) da declarao de um mtodo herdado o de toda a classe derivada e o alcance de validade da declarao dos mtodos exclusivos da classe derivada no abrange a classe base. A declarao de um mtodo exclusivo da classe derivada oculta na classe derivada a declarao do mtodo herdado da classe base. Ex: class B { public: void h(int); void w(); }; class D : public B { public: void h(char*); void w(); //Executando aces diferentes de B::w(). }; void f(D &d) { d.h(7); //Erro: D::(char*) oculta B::h(int). d.w(); //Ok. invoca D::w(). d.B::h(7); //Ok. Invoca B::h(int) d.B::w(); //Ok. Invoca B::w() d.h(Hellow); //Ok. Invoca D::h(char*). }

3.3.7. Derivao Mltipla A hereditariedade mltipla interessante no que se refere a reutilizao de cdigo, mas pode suscitar alguns problemas de ambiguidade: - Ambiguidade de membros herdados --> Tem de se explicitar o operador resoluo de alcance - Ambiguidade por duplicao de herana Problema do diamante --> encaminhar... ex: x= dd.B1::i A palavra chave virtual aplicada s classes (base) permite evitar duplicao de cpias quando mais que uma classe deriva da base. 3.3.8. Implementao handle/body da String com Derivao ... 3.3.9. Mtodos Virtuais e Polimorfismo Uma aco diz-se polimorfa se for executada de diferentes formas dependendo do contexto em que for invocada. Esse contexto pode ser determinado em tempo de compilao ou de execuo. O C++ utiliza vrios tipos de polimorfismo, nomeadamente overload de funes e de operadores, template de classe e funes (resolvido em tempo de compilao), e mtodos definidos como virtuais numa arborescncia de derivao (resolvido em tempo de execuo). Em C++, o polimorfismo traduz-se na possibilidade de redefinir nas classes derivadas os mtodos declarados na classe base como virtuais (com o mesmo nome, nmero e tipo de parmetros), por forma a que, quando invocado um mtodo virtual atravs de um apontador ou uma referncia para a classe base, a verso do mtodo posto em execuo seja o da classe do objecto apontado ou referenciado e no o da classe correspondente ao tipo da declarao do apontador ou da referncia como acontece nos mtodos comuns. Ex: class B { public: virtual char *f() {return B:.f(); } //Polimorfa char *g() {return B::g(); 0 //Normal }; class D : public B { public: char *f() 7return D::f() ; } //Polimorfa char *g() {return D::g() ; } //Normal }; void main () { D obj; B *pt = &obj; //pt do tipo apontador para B // mas aponta para um objecto D cout << pt-->f() << endl; //Escreve: D::f(). cout << pt-->g() << endl; //Escreve B..g(). } Na redefinio (overrride) de um mtodo declarado como virtual, preeciso manter a assinatura do mtodo. Se a sssinatura for diferente interpretado como overload. 3.3.10.2. Tags ...

CAP. 4 CONTENTORE SEQUENCIAIS 4.1. Introduo A norma ANSI/ISO da linguagem C++ e da respectiva biblioteca de templates STL, define os conceitos: Contentor para conter objectos Iterador para estabelecer ligao entre algoritmos genricos e contentores Allocator para isolar os contentores do detalhe dos modelos de alojamento de memria utilizados e para separar as aces de alojamento das de iniciao de objectos. Resumindo: Os algoritmos genricos usam contentores por intermdio dos iteradores e usam objectos funo na especializao dos algoritmos genricos. Os contentores usam os allocators para alojar e desalojar memria e para construir e destruir objectos. As normas ANSI/ISO no impem implementao especfica mas sim os mtodos pblicos a disponibilizar e a respectiva complexidade em tempo de execuo. Conforme os mtodos disponveis na sua interface, os contentores do STL tm diferentes nomes: vector, list, deque, set, map (tabela), stack, queue, priority_queue. Os tipos de contentores dividem-se em: - Contentores Sequenciais - Adaptadores de Contentores Sequenciais - Contentores Associativos 4.2. Generalidades Os contentores sequenciais suportam-se em estruturas de dados lineares, isto , aquelas em que todos os elementos contidos tm um nico elemento sucessor e um nico antecessor. permitem acesso sequencial aos elementos e disponibilizam operadores para inseres e remoes de objectos. 4.2.1. Tipos de Contentores Sequenciais T a[n] Estrutura array, predefinida na linguagem. Providencia acesso aleatrio a sequncias de dimenso fixa n. Os iteradores so apontadores comuns para objectos tipo T. Acesso em tempo constante a qualquer objecto, usando indexao ou aritmtica de apontadores. Inseres e remoes a meio e no incio requerem tempo linear. No fim requerem tempo constante. vector uma generalizao do array que, de modo transparente ao utilizador, aumenta a dimenso sempre que se torne necessrio. list suportada numa estrutura em lista duplamente ligada. Permite visita sequencial (em ambos os sentidos) a todos os objectos. Insero e remoo em tempo constante. deque Semelhante ao vector, excepto que permite inseres e remoes rpidas em ambos os extremos da sequncia. ring buffer vector em anel variante de fila de espera com capacidade constante preestabelecida FIFO. Objectos inseridos no fim da fila e removidos do incio, com tempo constante. 4.2.2. Complexidade em Notao Big-Oh Num algoritmo que toma como domnio um conjunto de n elementos, a notao Big-Oh exprime o tipo de proporcionalidade existente entre n e o tempo t(n) que demora a sua execuo. Por ex. a pesquisa de um elemento num array v no ordenado de dimenso n, requer no mximo n comparaes entre a chave de pesquisa e os contedos v[i] do array. Demorando k cada passo, vir: t(n) <= kn ----> tempo de pesquisa linear com n ou t(n) O(n) na notao Big-Oh. No caso de usarmos pesquisa dicotmica sobre um array ordenado de n elementos, o tempo de pesquisa seria: t(n) <= k log2n ou, em notao Big-Oh: t(n) O(log n). O que esta notao pretende realar o tipo de proporcionalidade que existe entre o nmero de elementos envolvidos num algoritmo e o seu tempo de execuo. Na biblioteca STL distinguem-se cinco tipos d complexidade: t(n) O (n2) - tempo quadrtico

t(n) O (n log n)) - tempo n log n


t(n) O (n) - tempo linear t(n) O (log n) - tempo logartmico t(n) O (1) - tempo constante A diferena de tempos concretos pode ser abissal. Nalguns casos melhor usar o tempo amortizado. Por ex. no push_back de um array, que insere no fim do array, mas quando no h mais espao dobra dinamicamente este, de forma automtica. Aloja espao para 2n elementos, muda para l os n anteriores e devolve espao anterior. Alojamento e cpia so O(n) e as inseres so O(1). Amortizando, podemos dizer que O(1)+ - algoritmo de tempo constante amortizado. 4.2.3. Iteradores Associados a Contentores Um objecto iterador, definido para um determinado contentor, comporta-se como um apontador inteligente para apontar para os objectos nele contidos, dispondo para tal de operadores que lhe permitem percorrer o conjunto desses objectos. Ex. operador incremento ++ ; e a sobrecarga do operador desreferncia * sobre um iterador retorna o objecto por ele apontado. No caso de contentores sequenciais (objectos ocupam bloco compacto e contguo de memria), os iteradores por excelncia so os prprios apontadores para esses objectos (T*). No caso de contentores sequenciais como a lista duplamente ligada, em que os objectos j no ocupam bloco compacto de memria, h que definir uma classe especfica que seja iterador e sobrecarregar os operadores ++, -- e * iterator &iterator::operator++(); //Avana o iterador iterator &iterator::operator(); //Recua o iterador T &iterator::operator*(); //Retorna a data iterada 4.2.3.1. Domnio de Iteradores em Contentores Sequenciais Os conceitos de domnio (range) e de iterador (iterator) so fundamentais em tudo o que vamos referir acerca dos contentores. Os algoritmos ganham acesso a uma dada sequncia de objectos, recebendo como argumentos um par de iteradores, first e last, do contentor onde esses objectos se situem. Designa-se por domnio de iteradores num contentor a sequncia de iteradores que apontam para uma sequncia de objectos contidos. Denota-se por [first, last[ . last aponta para a primeira posio a seguir ao ltimo objecto do domnio. Um domnio vlido se last for atingvel a partir de first, por incrementos sucessivos. O apontador last no pode ser desreferenciado mas pode entrar na aritmtica de apontadores. ex: const int MAX = 10; int v[MAX]; //Iterador sobre o array typedef int *iterator; //Iterador para o incio do domnio iterator begin() {return &v[0]; } //Iterador para o past the end iterator end() {return &v[MAX]; } //Afectar os elementos do domnio com o prprio ndice void assign(iterator first, iterator last) 7 for (int i=0; first != last; ++first, ++i) *first=i; } //Mostrar o valor dos elementos do domnio void write(iterator first, iterator last) { for(; first != last; ++first) cout << *first << , 0 void main () { assign (begin(), end()); write (begin(), end()); 0 4.2.3.2. Categorias de Iteradores O standard C++ exige que todos os operadores que constem da interface de uma classe iterator tenham complexidade O(1) ou constante amortizada O(1)+ e, assim, definem-se vrias categorias

de iteradores, conforme o conjunto de operaes que disponibilizam satisfazendo esta exigncia. Depende do contentor para o qual foi definido. As categorias so: Output, Input; Forward; Bidirectional; Random Access. Posso definir as funes do exemplo anterior como template usando a categoria adequada (mnima) mas que funcioan tambm se lhe forem passados parmetros de categorias superiores. 4.2.3.3. Seleco do Algoritmo Apropriado ao Iterador ex: template<class ItIn> unsigned distance (ItIn first, ItIn last) { unsigned res=0; while(first!=last) {++first; ++res; } return res; } Funciona para qualquer iterador da categoria input, logo funcioan para qualquer outro excepto output. Mas tem mau desempenho O(n). Se lhe forem passados iteradores de acesso aleatrio fica com O(1) pois estes permitem p-q em O(1). O ideal seria ter apenas um template de funes distance() que seleccionasse o algoritmo mais eficiente, conforme a categoria dos iteradores que lhe fossem passados como argumentos. Para o algoritmo genrico inferir a categoria do iterador, basta que na definio do iterador conste um membro tipo que possa ser testado, o que complicado dado que o que tem de ser testado se aa categoria do iterador igual ou maior que lhe necessria. A tcnica de utilizao de tags descrita no cap. anterior assume aqui a tarefa para que est vocacionada. struct output_iterator_tag {}; ... struct random_access_iterator_tag {}; Basta agora que qualquer classe de iterador tenha uma definio do tipo iterator_category, baseada nestes tipos de classes tag. Ex. classe iterator de list deve ter a seguinte declarao: class iterator { //Iterador de list bidireccional. public: typedef bidirectional_iterator_tag iterator_category; //... }; Tem pois agora coero implcita para input por exemplo quando um algoritmo necessite de um iterador da categoria input. H ainda que usar Traits para o caso de querermos usar o tipo pointer predefinido na linguagem como categoria de iterador random acess. ver pg. 192-194 para aprofundamento. 4.2.4. Gesto de Memria nos Contentores 4.2.4.1. Gesto de Memria na Forma Trivial Conhecemos 3 modos de alojamento de objectos em memria: - Esttico: quando os objectos (estticos) so alojados na memria de dados globais, durante o carregamento do programa (load time); - Automtico: quando os objectos (automticos e temporrios) so alojados em run time no stack: - Dinmico: quando os objectos (dinmicos) so alojados e desalojados na memria livre (heap). ex. em T a(10) reservada memria esttica ou automtica. Quando o programa sai do scope da declarao, invocado implicitamente o destrutor de T e a memria libertada. No declarativa como x=T(10) --> automtica ... 4.2.4.2. Operadores new e delete bsicos Quando invocado abreviadamente: T *p = new T(10) o operador new reserva espao no heap para um objecto T e posto implicitamente em execuo o construtor. delete p posterior invocado implicitamente o destrutor. Quer o operador new, quer o delete cujas assinaturas so: void *operator new(size_t n); operator delete (void *p); podem ser tambm invocados explicitamente no programa: T p * = ( T *) :: operator new(sizeof(T));

..operator delete(p);

Este tipo de invocao promove comportamentos diferentes da forma abreviada, pois limita-se a reservar espao no heap sem pr em execuo o construtor. 4.2.4.3. Gesto da Memria nos Contentores Standard A STL diferente. Adopta como critrio separar as aces de reserva de espao, das aces de iniciao de objectos (2 mtodos diferentes). Da mesma a devoluo do espao fica dissociada da destruio dos objectos. A vantagem a eficincia, como se mostra mais frente para o template de classes vector. Este novo critrio implica a sobrecarga do operador new, usando mais um parmetro adicional (apontador para void) para permitir construir um objecto num endereo explcito da memria, previamente reservado --> tcnica do placement syntax. void *operator new (size_t, void *p) {return p; } e limita-se a retornar o prprio apontador que lhe foi passado sem reservar nenhuma reserva de espao. Assim, new(p) T(10) s contri, no espao anteriormente reservado pela invocao explcita do new bsico. ... 4.3 Template de Classes Allocator Todos os contentores genricos da biblioteca tomam como segundo parmetro uma classe template allocator<T> que determina o modo como a memria gerida. Esse template tem uma interface satndard de tipos e mtodos que todos os componentes da biblioteca tomam como auxiliares, quer para reservar e devolver memria no iniciada, quer para construir e destruir objectos. Um allocator providencia mtodos para alojar e desalojar memria, e mtodos pblicos para construir e destruir objectos. Isola assim contentores e algoritmos da gesto de memria, para alm de definir nomes de tipos e mtodos com que nos devemos pouco a pouco familiarizar. Veremos a seguir como se faz, na classe vector. 4.3.1. Requisitos de um Allocator ... 4.4. Template de Classes Container ...

4.5. Template de Classes vector Principais caractersticas: - Permitir acesso aleatrio em tempo constante O(1) aos objectos contidos, por indexao ou aritmtica de apontadores; - Permitir inseres e remoes em tempo constante amortizado O(1)+ no fim da sequncia; - Permitir inseres e remoes em no incio ou no meio da sequncia em tempo linear O(n) - Ampliar automaticamente a capacidade da representao em memria, por realojamento. No caso duplicar ou criar unidade se zero. A definio de um template de contentor envolve sempre a definio de um iterador que lhe seja especfico. No caso um simples apontador. Um vector dispe de 3 atributos do tipo iterator: start, finish e endOfStorage.

4.5.1. Definio do Template de Classes vector


#define SUPER Container<T,A> template<class T, class A = allocator<T>> class vector: public SUPER { public: IMPORT_TYPE(pointer); IMPORT_TYPE(const_pointer); IMPORT_TYPE(reference); IMPORT_TYPE(const_reference); IMPORT_TYPE(size_type); //Tipos no herdados de Container<T,A> typedef pointer iterator; typedef const_pointer const_iterator; //Construtores e destrutor explicit vector(const A & = A()); //Default vazio //Iniciado com n objectos tipo T explicit vector(size_type, const T & =T(), const A & =A); vector (const vector&); //Por cpia ~vector(); //Invoca destrutor de A //Sobrecarga do operador afectao vector &operator=(const vector&); //Mtodos de acesso //Acesso s dimenses size_type capacity() const {return endOfStorage-begin(); } size_type size() const 7return end() begin(); } size_type max-size() const {return allocator.max_size(); } bool empty() const {return size() = 0; } const A &get_allocator() const {return allocator;} //Acesso por iterador iterator begin() {return start; } iterator end() {return finish; } const_iterator begin() const {return start; } const_iterator end() const {return finish; } //Acesso ao elemento reference front() {return (*begin()); 0 reference back() {return (*end()-1)); } reference operator[] (size_type i) {return (*begin() + i)); } //Mtodos de insero e remoo de elementos iterator insert(iterator p, const T &x); iterator (erase (iterator p); void push_back (const T &x) {insert(end(), x); } void pop_baxck () {erase(end() 1); } //Outros mtodos void reserve(size_type n);

void clear(); void swap(vector &x); private:

//Atributos
A allocator; //allocator<T> por omisso iterator start, finish, endOfStorage; //Mtodos auxiliares void destroy(iterator start, itrator finish); iterator uninitializedCopy(const_iterator first, cons_iterator last, iterator p); iterator uninitializedFill(iteartor p, size_type n, const T &x); }; //Fim da definio do template de classes vector #undef SUPER 4.5.1.1. Construtores Todos os construtores constroem por cpia o atributo allocator. O construtor sem parmetros constri um vector vazio, colocando os atributos start, finish e endOfStorage a NULL, e no providencia reserva de espao. template<class T, class A> vector<T,a>::vector(const a &a1) : allocator(a1), start(), finish(), endOfStorage() {} template<class T, class A> vector<T,A>::vector(size_type n, const T &x, const AS &a1) : allocatro(a1) { start=allocator.allocate(n); //Pedir memria //Construir n elementos por cpia endOfStorage = finish = uninitializedFill(star, n, x); } O construtor declarado explicit para que no promova a coero automtica do tipo size_type para tipo vector template<class T, class A> vector<T,A>::iterator vector<A,T>::uninitializedFill(iterator dest, size_type n, const T &x) { for(; n; --n, ++dest) allocator.construct(dest, x); return dest; } template<class T, class A> vector<T,A>::vector(const vector &x) : allocator(x.allocator) { start = allocator.allocate(x.size()); finish = uninitializedCopy(x.begin(), x.end(), start); endOfStorage = finish; } template<Class T, class A> vector<T,A>::iterator vectro<T,A>::uninitializedCopy(iterator first, iterator last, iterator dest) { for(;first!=last; ++dest, ++first) allocator.construct(dest, *first); return dest; } 4.5.1.2. Destrutor O mtodo destrutor destri todos os objectos contidos no vector e liberta memria reservada, invocando o mtodo deallocate() sobre o objecto allocatro. template<class T, class A> vector<T,A>::~vector() { destroy(begin(), end()); allocator.deallocate(begin(), capacity()); } O mtodo auxiliar destroy() destri os objectos do domnio [first, last[, invocando o mtodo homnimo de allocator (pg.213)

4.5.1.3. Mtodo reserve() O mtodo reserve(size_type n) garante que, aps ser invocado, a capacidade do vector maior ou igual a n. (pg.213) 4.5.1.4. Operador Afectao Requer cuidados especiais, devido ao facto de termos separado a reserva da iniciao. - Caso haja necessidade de expanso do espao de memria, destri os objectos contidos, liberta a memria reservada, reserva um novo bloco de memria e constri, nesse bloco, objectos por cpia dos contidos no vector a copiar. - No caso contrrio destri os restantes elementos do vector a afectar. (pg.214) 4.5.1.5. Mtodo insert() Tambm requer cuidados: template<class T, class A> vector<T,A>::iterator vector<T,A>::insert( iterator p, const T &x) { if ( endOfStorage == end() ) { //No existe espao //Dimenso do antigo e do novo espao size_type sz=size(), n=sz + (1 < sz ? sz : 1); //Pedir novo espao iterator aux = allocator.allocate(n); //Construir por cpia no novo espao elementos at p iterator newp=uninitializedCopy(begin(), p, aux); //Construir o elemento a inserir por cpia allocator.construct(newp, x); //Construir por cpia no novo espao os elementos depois de p. uninitializedCopy(p, end(), newp+1); //Destruir os elementos do espao anterior destroy(begin(), end()); //Libertar o espao anterior allocator.deallocate(begin(), capacity()); endOfStorage = aux + n; finish = aux + sz + 1; start = aux; return newp; //fica a apontar para o inserido. } if (p==end()) //Inserir no fim //Construir o elemento a inserir por cpia allocator.contruct(end(), x); else { //Inserir no meio //Deslocar o ltimo, construindo por cpia copy_backward(p, end()-1, end()); *p=x; //Copiar o elemento a inserir } ++finish; return p; } template<class ItBi1, class ItBi2> ItBi2 copy_backward(ItBi1 first, ItBi1 last, ItBi2 res) { while (first!=last) *--res = *--last; return res; } 4.5.1.6. Mtodo Erase() Desloca o domnio [p+1, end()[ para o domnio [p,end()-1[ e destri o ltimo elemento do vector. template<class T, class A> vector<T,A>::iterator vector<T,A>::erase(iterator p) {

finish = copy (p+1, end(), p); //Desloca elementos (esmaga o que para apagar). Retorna iterador para o elemento past the end copiado. allocator.destroy(finish); //Destruir o excedente return p; } 4.5.1.7. Mtodo clear() template<class T, class A> void vector<T,A>::clear() {destroy(begin(), end()); finish = start; } 4.5.1.8. Mtodo swap() template<class T, class A> void vector<T,A>::swap(vector &vx) { if (this != &X) if (allocator == x. allocator) { ::swap(start, x.start); ::swap(finish, x.finish); ::swap(endOfStorage, x.endOfStorage); } else ::swap(*this, x); template <class U> inline void swap(U &a, U &b) {U aux(a); a=b; b=aux; }

4.6. TEMPLATE DE CLASSES LIST uma estrutura linear de elementos (Nodes), encadeados entre si atravs de apontadores. A biblioteca standard tem listas duplamente ligadas com sentinela, o que permite iteraes sequenciais nos dois sentidos. O n sentinela simplifica os algoritmos tornando-os mais eficientes. A interface muito parecida com o template de vector, diferindo no que toca ao desempenho dos mtodos. As diferenas fundamentais so: - Na list a memria para um novo Node automaticamente reservada quando se insere um objecto e automaticamente devolvida ao gestor de memria (allocator) sempre que se remove um objecto. - a list tem desempenho em tempo constante para remoes e inseres em qualquer ponto da sequncia para o qual se disponha previamente um iterador.

4.6.1. Definio do template de classes list


#define SUPER Container <T,A> template<class T, class A=allocator<T>> class list: public SUPER { public: IMPORT_TYPE(size_type); IMPORT_TYPE(difference_type); IMPORT_TYPE(reference); IMPORT_TYPE(const_reference); IMPORT_TYPE(pointer); IMPORT_TYPE(const_pointer); private: struct Node; //Define ANode como tipo allocator de Node, da mesma famlia template da qual A o tipo allocator para T. typedef typename A::rebind<Node>::other ANode; typedef typename Anode::pointer NodePtr; typedef typename Anode::const_pointer ConstNodePtr; struct Node { NodePtr prev; NodePtr next; T data; }; public: //Definio dos iteradores class const_iterator {...}; //Definidos adiante class iterator {...}; //Os atributos da lista sero os seguintes: private: A constr; //Allocator para construir objectos T ANode alloc; //Allocator para alojar e desalojar nodes NodePtr head; //Apontador para o n sentinela (dummy) size_type sz; //nmero de objectos contidos //Mtodos auxiliares //Aloja o n sentinela e inicia os membros e apontadores a apontarem para ele prprio NodePtr newNode(); //Aloja e inicia um node com e, e promove o encadeamento dos apontadores posicionando-o atrs de suc NodePtr newNode(NodePtr suc, const T &e); //Desliga da lista o Node apontado por p e destri-o void deleteNode(NodePtr p); //Move um bloco entre duas listas se allocators iguais void moveInAlloc (iterator p, iterator f, iterator l); //Move um bloco entre 2 listas void move(iterator p, list&, iterator f, iterator l); Interface Pblica public:

//Construtores e destrutor explicit list(const A &a1=A()); //Lista iniciada com n objectos cpia de k explicit list(size_type n, const T &k=T, const A &a=A()); list(const list &lst); ~list() {clear(); alloc.deallocate(head,1); } //Afectaes //Afectao com os objectos do domnio [first, last[ template<class InIt> void assign(InIt first, InIt last); //Afectao com os objectos doutra lista do mesmo tipo list &operator=(const list &lst); Obteno de iteradores iterator begin() {return head-->next; } iterator end() {return head;} //Dimenses size_type size() const {return sz;} bool empty() {return sz==0;} const A &get_allocator() const {return constr;} //Acesso aos elementos reference front() {return *begin(); } reference back() {return *(--end()); } //Inseres e remoes //Insere na posio anterior a p, um n por cpia de e iterator insert( iterator p, const T &e = T()); } //Insere na posio anterior a p, os objectos situados no domnio [first, last[ template<class InputIter> void insert(iterator p, InputIter first, InputIter last); void push_front(const T &e) {insert (begin(), e); } void push_back(const T &e) {insert(end(), e); } //Remove da lista o n apontado por p iterator erase(iterator p); //Remove os ns contidos no domnio [first, last[ iterator erase(iterator first, iterator last); void pop_front() {erase(begin()); } void pop_back() {erase(--end());} //Trocar o contedo entre duas listas void swap(list lst); //Interface especializada da lista (s existem na list) //Mover os elementos do domnio [f,l[ ou first da lista lst, para a posio anterior a p, na lista *this; //mover implica inserir numa lista e remover da outra void splice(iterator p, list &lst, iterator f, iterator l); void splice(iterator p, list &lst, iterator first); //Pressupe listas ordenadas. Insere ordenadamente todos os elementos de lst, deixando-a vazia void merge(list &lst); void sort(); //Ordena os elementos da lista }; fim da definio do template de classes list #undef SUPER 4.6.1.1. Iteradores Dado que os ns da lista ocupam posies dispersas na memria, passar para o n seguinte no se traduz em incrementar simplesmente o apontador. Torna-se necessrio definir uma classe iterator, especfica para iterar em list. ver como em pg.222 e seguintes. Faz sobrecarga dos opeardores que permitam classific-lo como bidireccional. Assim, alm dos operadores de desreferncia e afectao deve dispor tambm dos operadores de incremento e decremento (prefixos e sufixos). No dispe dos operadores += e -= pois no so de tempo constante. O construtor com um apontador para n constri um iterador para esse n.

A classe iterator para lista nested, interna, lista. Tem um nico atributo: NodePtr ptr; Tem um mtodo NodePtr current() const {return ptr;} 4.6.1.2. Mtodos auxiliares newNode() A verso newNode() sem parmetros auxiliar dos construtores de listas tomando para si a tarefa de alojar e iniciar o n dummy, colocando os apontadores next e prev a apontar para o prprio Node. O membro data no precisa ser iniciado. template<class T, class A> list<T,A>::NodePtr list<T,A>::newNode() { NodePtr n = alloc.allocate(1); //Aloja o n dummy e return n->next = n->prev = n; //inicia os apontadores. //Construtor de lista vazia template<class T, class A> inline list<T.A>::list(const A &a1) : constr(a1), alloc(a1), head(newNode()), sz(0) {} A verso newNode() com 2 parmetros auxiliar de todos os mtodos que necessitam criar e inserir na lista objectos Node. Ela reserva espao par um n, constri no campo data um objecto T por cpia do objecto cuja referncia lhe seja passada como segundo parmetro e, finalmente, promove o seu encadeamento na posio anterior do n cujo apontador lhe seja passado como primeiro parmetro. template<class T, class A> list<T,a>::NodePtr list<T,A>newNode(NodePtr suc, const T &e) { NodePtr n = alloc.allocate(1); constr.construct(&(n->data), e); NodePtr prev = suc ->prev; n->next = suc; n->prev = prev; prev->next = suc->prev = n; return n; } 4.6.1.3. Mtodos de Insero Resulta extremamente simples, suportada no mtodo newNode() com 2 parmetros, que vimos antes. Insere na posio anterior a p. template<class T, class A> list<T,A>::iterator list<T,A>::insert(iterator p, const T &e) { ++sz; return newNode(p.current(), e); } Insere na posio anterior a p, os objectos situados no domnio [first, last[ de outro contentor qualquer template<classT, class A> template<class InIter> void list<T,A>::insert(iterator p, InIter first, InIter last) { for(; first != last; ++first) insert(p, *first); } 4.6.1.4. Construtores com n objectos e por cpia 4.6.1.5. Mtodo deleteNode() Desliga da lista o n a remover, afectando adequadamente o membro next do n antecessor e o membro prev do n sucessor. Seguidamente invoca o mtodo destroy sobre constr para destruir os dados sem libertar a memria e o mtodo deallocate sobre alloc para desalojar o n (devolver o espao de memria) template<class T, class A> void list<T,A>::deleteNode(NodePtr n) { n->prev->next = n->next; n->next->prev = n->prev; constr.destroy(&(n->data)); alloc.deallocate(n,1); }

4.6.1.6. Mtodos de Remoo template<class T, class A> list<T,A>::iterator list<T,A>::erase(iterator p) { deleteNode(p++).current()); --sz; return p; } 4.6.1.7. Operador afectao e mtodo assign() 4.6.1.8. Mtodo swap() 4.6.1.9. Mtodo splice() 4.6.1.10. Mtodo merge() 4.6.1.11. Mtodo sort()

4.7. Template de Classes deque Pode ser descrito como uma fila de espera com insero e remoo rpida em ambos os extremos. Tal como a lista, vocacionado para cumprir a disciplina FIFO ou FILO mas permite acesso aleatrio como o vector. Os contentores deque constituem uma verso mais verstil da estrutura de dados queue, muito utilizada em programao e que se suporta num deque restringido a FIFO; um adaptador. Comparando deque com list e vector: - tal como o vector e contrariamente list, o deque permite acesso aleatrio a todos os objectos contidos (embora em tempo constante so mais lentas que no vector). - Contrariamente ao vector, permite realizar, em tempo constante, aces de insero e remoo em ambos os extremos- tambm contrariamente ao vector, permite que a memria reservada diminua de dimenso quando predominam as aces de remoo. - Enquanto a list permite inseres e remoes em tempo constante no meio do seu domnio, o deque, tal como o vector, s permite executar essas aces em tempo linear. 4.7.1. Estrutura de Dados de Suporte Este contentor suporta-se em blocos de memria (DataBlock) de dimenso fixa. Em tempo de execuo, o espao reservado para o deque pode ser ampliado em ambos os sentidos, associando-lhe novos DataBlock. Os apontadores para os DataBlock situam-se, centrados e ordenados, num outro bloco de memria de comprimento varivel que denominamos MapBlock. O template de classes deque tem como atributos dois iteradores: start e finish - cada iterador tem 4 apontadores -> h uma figura muito elucidativa na pg. 243 se me esquecer. Tem ainda como atributos map, que aponta para o MapBlock e mapSize que memoriza a dimenso actual do MapBlock. Adopta-se 5 como o n mnimo para a dimenso do MapBlock. A dimenso dos dataBlock situa-se normalmente na ordem dos 100 elementos. 4.7.2. Exemplos da Construo de um Deque queremos construir um deque<int> iniciado com 8 objectos tipo int, cpias do inteiro 0 e no qual, para facilitar, restringimos a 5 a dimenso dos DataBlock (DIM_BLOCK = 5) O critrio para organizar a estrutura de dados a seguinte: - Infere-se quantos DataBlock devem ser criados para iniciar o deque com 8 objectos; - Reserva-se espao para um MapBlock, satisfazendoo mnimo de 5 (DIM_MAP_MIN=5) e de poder armazenar um n de apontadores duplo do n de DataBlock a criar. - Reserva-se o n de DataBlock inferidos e situam-se, centrados no MapBlock, os respectivos apontadores. Os critrios de inferncia acima citados so: - O primeiro objecto a inserir num deque deve situar-se a meio do primeiro DataBlock, para push_back() e push_front() posteriores. - Quando o n de objectos a inserir sequencialmente no DataBlock corrente vierem a ocupar a ltima posio disponvel, deve-se criar mais um DataBlock (o que proporciona maior eficincia nos mtodos, por simplificao dos algoritmos). - Sempre que se cria um MapBlock para n apontadores para DataBlock, reserva-se sempre um espao duplo do necessrio (2*n) e inserem-se os n apontadores centrados nesse espao, prevendo futuras inseres, tanto em posies anteriores como posteriores s ocupadas. Segundo os critrios definidos: numOPos = numOfObj + DIM_BLOCK/2 + 1; // numOfPos = 8+5/2+1 =11 numOfBlocks = numOfPos/DIM_BLOCK + (numOfPos%DIM_BLOCK !=0); //numOfBlocks=11/5 + 1=3 mapSize = (numOfBlocks < (DIM_MAP/2))? DIM_MAP:numOfBlocks*2; mapSize = (3 < 5/2)? 5 : 6 = 6

Definio do Template de Classes Deque


#define SUPER Container<T,A> template <class T, class A = allocator<T>> class deque : public SUPER { public: IMPORT_TYPE(pointer); IMPORT_TYPE(const_pointer; IMPORT_TYPE(reference); IMPORT_TYPE(const_reference); IMPORT_TYPE(size_type); IMPORT_TYPE(difference_type); private: //<<Constantes necessrios>> static const size_type DIM_MAP_MIN = 5; static const size_type DIM_BLOCK=512/sizeof(T); //<<Tipos necessrios a deque e ao iterador>> typedef typename A::rebind<pointer>::other AMap; typedef typename AMap::pointer MapPointer; template <class MapPtr, class PTR, class REF> class IT {...}; public: //<<Tipos de Iteradores>> typedef IT<MapPointer, pointer, reference> iteartor; //<<Atributos>> private: A blockAlloc; AMap mapAlloc; MapPointer map; size_type mapSize; iterator start, finish; //<<Interface Pblica>> public: //<<Construtores, destrutor e operador afectao>> //Constri um deque vazio explicit deque(const A &a1=A()); //Constri um deque com n cpias de t explicit deque(size_type n, const T &t=T(), const A &a1=A()); deque(const deque &x); ~deque(); deque &operatro=(const deque &x); //<<Mtodos de acesso>> //Acesso s dimenses size_type size() const {return finish start;} bool empty() const {return start==finish;} //Acesso por iterador iterator begin() {return start;} iterator end() {return finish;} //Acesso a elementos //Retorna uma referncia para o objecto indexado reference operator[](size_type i) {return begin() [i];} //Retorna o objecto apontado por start.current reference front() {return *begin();}

4.8 Template de Classes RING BUFFER


uma variante da queue de dimenso fixa, destinada a manter em memria os ltimos n elementos que lhe tenham sido inseridos. Tal como a queue um contentor com disciplina FIFO em que os objectos so inseridos no fim da fila e removidos no seu incio. Os mtodos so: push_back(T) insere no fim da fila pop_front() remove no incio da fila front() acede ao elemento do incio para leitura back() acede ao elemento do fim da fila para escrita O RingBuffer implementado num array com dimenso fixa. Quando o array est cheio e se insere um novo elemento, origina-se o overflow e o elemento que est no incio da fila perdido por esmagamento. O par de iteradores start e finish mantm a dinmica de insero e remoo de forma similar aos iteradores de um deque. A remoo de um objecto do ring buffer limita-se a avanar o iterador start, pois o removido sempre o do incio da lista. O array deve ter sempre uma posio no usada, de modo a que o teste de contentor cheio se distinga do teste de contentor vazio. Assim, testar o contentor vazio resume-se a verificar se os iteradores start e finish apontam para o mesmo objecto. A operao de insero que providencia que os iteardores no apontem para o mesmo objecto se o contentor estiver cheio. O iterador do RingBuffer start, tem 2 atributos: array aponta para o incio do array; current aponta para o elemento 1 do RingBuffer; no finish, array aponta igualmente para o incio do array e current aponta para o past the end.

4.8.1. Definio do template RingBuffer


#define SUPER Container<T,A> template <class T, unsigned DIM, class A=allocator<T>> class RingBuffer : public SUPER { public: IMPORT_TYPE(pointer); IMPORT_TYPE(const_pointer); IMPORT_TYPE(reference); IMPORT_TYPE(const_reference); IMPORT_TYPE(size_type); //<<Tipo para a diferena entre iteradores>> typedef CircCount<DIM + 1> difference_type; private: template<class PTR, class REF> class IT {/*...*/}

Interface Pblica
public: //<<Instanciao de Iteradores>> typedef IT<pointer, reference> iterator; typedef IT<const_pointer, const_reference> const_iterator; //<<Construtores e destrutor e operador afectao>> RingBuffer(const A &a=A()); RingBuffer(const RingBuffer&); ~RingBuffer(); RingBuffer &operator=(const RingBuffer &); //<<Mtodos de acesso>> //Acesso s dimenses size_type size() const {return finish-start;} size_type max_size() const {return DIM;} bool empty() const {return start==finish; } const A &get_allocator() const {return allocator;} //Acesso por iterador iterator begin() const {return start;} iterator end() const {return finish;} //Acesso aos extremos reference front() {return *begin();}

reference back() {return *--end();} reference operator[](size_type idx) {return begin()[idx];} //<<Insero e Remoo>> void push_back(const T&); void pop_front(); //<<Outras operaes>> void clear() {destroy(start, finish); finish=start} private:

//<<Atributos>>
A allocator //Allocator para o array circular iterator start; iterator finish; //Mtodos auxiliares iterator uninitializedCopy(const_iterator first, const_iterator last, iterator res); void destroy(iterator first, iterator last); }; #undef SUPER 4.8.1.1. Iterador O iterador do RingBuffer tem como atributos o ndice corrente do tipo CircCount (contador circular com mdulo de contagem) e um apontador para o array, de forma a ser possvel as operaes de desreferenciao e indexao.

4.8.1.2. e 4.8.2.4. Construtores e Destrutor e Operador Afectao


Construtor por omisso RingBuffer(const A &a=A()) : allocator(a), start(allocator.allocate(DIM+1)), finish(start) {} Construtor por cpia RingBuffer(const RingBuffer &r) : allocator(r.allocator), start(allocator.allocate(DIM+1)), finish(uninitializedCopy(r.begin(), r.end(), begin())) {} com: iterator uninitializedCopy(const_iterator first, const_iterator last, iteartor res) { for(; first!=last; ++first, ++res) allocator. construct(&(*res), *first); return res; } Destrutor ~RingBuffer() { destroy(start, finish); allocator.deallocate(start.array, DIM+1); } com: void destroy(iterator first, iteartor last) { for(;first!=last; ++first) allocator.destroy(&(*first));}

4.8.1.3. Mtodos de Insero e Remoo


void push_back(const T &t) { allocator.construct(&(*finish, t); if(++finish==start) pop_front(); } void pop_front() { allocator.destroy(&(*start)); ++start; }

CAP: 5 RVORES BINRIAS As estruturas em rvore um dos tpicos mais importantes de EDA Embora no sejam componentes standard da biblioteca C++, a famlia dos contentores associativos suportam-se em rvores binrias de pesquisa balanceadas e o adaptador de contentores sequenciais priority_queue tem como modelo conceptual uma estrutura em rvore completa. Nas rvores cada n pode ter um n arbitrrio de sucessores. rvore: a coleco de ns cuja posio e informao contida satisfazem um determinado conjunto de propriedades Ns so os objectos constituintes das rvores Ramos so as ligaes entre os ns Raiz o n de onde emergem, directa ou indirectamente, todos os ramos. Percurso um caminho possvel entre 2 ns, satisfazendo uma dada condio Nvel de um n o nmero de ramos envolvidos no percurso entre a raiz e o n. Altura o mximo nvel (a raiz o nvel 0) Ascendente (ou pai) Descendentes directos (filhos) Folhas ou ns terminais Uma rvore diz-se ordenada, quando o posicionamento relativo dos descendentes de cada n significativo. Uma rvore binria aquela em que cada n tem no mximo dois descendentes directos. Uma rvore binria de pesquisa (ABP) uma rvore binria ordenada, em que o valor contido em qualquer n maior ou igual que os valores contidos nos ns da sua subrvore esquerda e menor ou igual que os valores da sua subrvore direita. Uma rvore diz-se balanceada quando a diferena de alturas das duas subrvores de qualquer n menor que 1. Uma ABP quase balanceada (rvore red-black), no pior caso a altura de uma das subrvores nunca ultrapassa o dobro da outra, para todos os ns. Uma rvore diz-se perfeitamente balanceada se, relativamente a qualquer n, a diferena entre o nmero de ns das suas subrvores for no mximo 1. Diz-se completa se estiver inteiramente preenchida, isto , se todos os ns tm ambos os filhos, excepto no ltimo nvel que vai sendo preenchido da esquerda para a direita. Diz-se organizada em heap (monte) caso seja completa e todos os ns tiverem a propriedade do seu valor ser maior ou igual a qualquer dos seus filhos. 5.2. ABP O desempenho da pesquisa aproxima-se tanto mais da pesquisa dicotmica sobre vectores (ordem log N), quanto mais balanceadas estas estiverem. Os ns so constitudos por objectos de uma classe, com os atributos: - O valor a armazenar - Um apontador para a sua subrvore esquerda - Um apontador para a sua subrvore direita No caso de no existir alguma subrvore, o respectivo apontador toma o valor NULL. Associada a uma estrutura de ns em rvore define-se um template de classes Tree, gestoras dessa estrutura, cujo atributo fundamental um apontador para o n raiz, dispondo de mtodos pblicos (inserir n, remover n, aceder a todos os ns com um dado critrio de percurso.) 5.2.1. Verso Bsica de ABP Adoptando um n dummy e acrescentando estrutura dos ns um apontador para o seu ascendente, possvel definir (como prescreve o standard C++) um iterador bidireccional. Os exemplos so parecidos com o RBTree da biblioteca. #define SUPER Container<T,A> template<classT, class A = allocator<T>> class TreeBase : public SUPER { protected: //<<Tipos usados pela rvore>> struct Node;

typedef typename A::rebind<Node>::other ANode; typedef typename ANode::pointer NodePtr; typedef typename Anode::const_pointer ConstNodePtr; struct Node { T values; NodePtr Left; NodePtr right; NodePtr parent; }; //<<Declarao dos iteradores template<class NodePtr, class PTR, class REF> class IT; template<class NodePtr, class PTR, class REF> friend class IT; public: IMPORT_TYPE(reference); IMPORT_TYPE(const_reference); IMPORT_TYPE(pointer); IMPORT_TYPE(const_pointer); IMPORT_TYPE(size_type); IMPORT_TYPE(difference_type); typedef IT<NodePtr, pointr, reference> iterator; typedef IT<ConstNodePtr, const_pointer, const_reference> const_iterator; protected: //<<atributos>> A constr; //Allocator para construir objectos T ANode alloc; size_type sz; NodePtr dummy; NodePtr root; //Mtodos auxiliares declarados na parte protegida //<<Mtodos de instanciao e destruio de ns>> //Aloja n sentinela e inicia membros apontadores a apontarem para ele prprio NodePtr newNode(); //Auxiliar de insert(). Aloja um Node e inicia value com e, left e right com NULL e parent com p NodePtr newNode(NodePtr p, const T &e); //Auxiliar de copy(). Aloja um Node e constri value por cpia de p.value NodePtr newNode(ConstNodePtr p); //Destri e desaloja o Node apontado por p void deleteNode(NodePtr p); //<<Mtodos que os mtodos pblicos homnimos invocam>> //Template de mtodos de percursos; Visit um objecto funo ou apontador para funo; determina a aco a executar nos ns visitados template<class Visit> void preorder(NodePtr r, Visit) const; template<class Visit> void inorder(NodePtr r, Visit) const; template<class Visit> void postorder(NodePtr r, Visit) const; //<<Mtodos auxiliares de insero e remoo>> //Se MULTI for true executa o insertMulti(), se falso insertUni() template<bool MULTI> pair<iterator, bool> insertNode(const T&); //Insere um n esquerda de r. Retorna o n inserido NodePtr insertLeft(NodePtr r, const T &e); NodePtr insertRight(NodePtr r, const T &e); //Afecta parent de descendente com ascendente static void setParent(NodePtr child, NodePtr father); //Auxiliar de erase(). o parent de x passa a parent de y void transferParent(NodePtr x, NodePtr y);

//Auxiliar de erase(), o n x substitudo pelo n y. O parent de x passa a parent de y. as subrvores direita e esquerda de x passam a subrvores direita e esquerda de y void transferNode(NodePtr x, NodePtr y); //Destri a rvore ou subrvore com raiz Em r void clear(NodePtr r); //Constri uma rvore por cpia da rvore com raiz em r. Retrorna apontador para raiz da cpia NodePtr copy(const TreeBase &x); //Retorna o n mais direita da subrvore r. template<class NP> static NP rightMost(NP r); template<class NP> static leftMost(NP r); //Auxiliar do mtodo pblico isBalance(). static bool isBalanced(ConstNodePtr, int &); //Converter a rvore em lista; auxiliar do mtodo balance() static void treeToList(NodePtr r, NodePtr &header); //Converter lista em rvore; auxiliar do mtodo balance() static NodePtr listToTree(NodePtr &header, size_type n); //<<Converso de Iteradores em apontadores e vice versa. Estes mtodos auxiliares podero ser necessarios s classes derivadas, que no sendo friend do template de classes IT, no tm acesso aos construtores nem ao atributo current. iterator getIterator(NodePtr p) {return p; } NodePtr getNode(iterator i) {return i.current; } //Interface Pblica public: //<<Construtores e Destrutor TreeBase(const A &a = A()) : alloc(a), constr(a), root(NULL), dummy(newNode()), sz(0) {} TreeBase(const TreeBase &x); ~TreeBase(); //<<Sobrecarga do operador afectao TreeBase &operator=(const TreeBase &x); //<<Cpia do allocator usado na construo>> A get_allocator() const {return constr); } //<<Percursos sobre a rvore>> /*Executar a aco imposta pela funo visit() sobre o atributo value dos sucessivos ns visitados, segundo cada um dos modos de percurso template<class Visit> void preorder(Visit visit) const {preorder(root, visit); } template<class Visit> void inorder(Visit visit) const {inorder(root, visit); } template<class Visit> void postorder(Visit visit) const {postorder(root, visit); } //<<Obteno de Iteradores iterator begin() {return dummy->left; } iterator end() {return dummy; } //<<Acesso s dimenses size_type size() const {return sz; } bool empty() const {return size()==0; } //<<Acesso aos elementos reference front() {return *begin(); } reference back() {return *(--end()); } //<<Mtodos de pesquisa, insero e remoo>> //Pocura um n cujo valor seja n. Se existir retorna um iterador para esse n. Casso contrrio retorna end(), que o dummy. iterator find(const T &n); //Insere ordenadamente (???) o valor n, caso ainda no exista

pair<iterator, bool> insertUni(const T &n); //Insere ordenadamente o valor n, mesmo que j exista iterator insertMulti( const T &n); //Remove da rvore o n cujo valor seja n, caso exista size_type erase(const T &n); //Remove da rvore o elemento apontado por i void erase(iterator i); //Remove todos os elementos void clear() //Promove o balanceamento da rvore void balance(); //Mtodos auxiliares de debugging //Mostra ordenadamente os valores contidos na rvore void display() const; //Testa se a rvore est de facto ordenada bool isOrded() const; //Testa se a rvore est balanceada bool isBalanced() const; //Mostra a topologia da rvore usando espaamentos horizontais por nveis void printOn(ostream &o) const; }; 5.2.1.1. Percursos prefixo, infixo e sufixo Percorrer uma rvore consiste em visitar todos os seus ns por uma determinada ordem, entendendo-se por visitar um n realizar algum tipo de aco sobre o valor que lhe esteja associado (contar o n total de objectos inseridos na rvore, mostrar no ecr esses valores, etc.) - O percurso prefixo (preorder) visita o n raiz, depois percorre a subrvore esquerda e, finalmente, percorre a subrvore direita; - Infixo: Esquerda, Raiz, Direita - Sufixo: Esquerda, Direita, Raiz Numa rvore de pesquisa ordenada de forma crescente tem relevncia o percurso infixo, dado que ser esse o percurso usado pelo iterador para percorrer por ordem crescente. Em qualquer dos tipos de percursos, aco a executar sobre cada n determinada pelo parmetro template Visit (objecto funo ou apontador para funo) //Percurso preorder template<class T, class A> template<class Visit> void TreeBase<T,A>::preorder(NodePtr r, Visit visit) const { if(r==NULL) return; visit(r->value); preorder(r->left, visit); preorder(r->right, visit); Fazer percurso inorder e postorder. Mtodo pblico display template<class T> void displayValue(const T &t) {cout << t << ;} template<class T, class A> void TreeBase<T,A>::display() const {inorder(displayValue<T>()); } 5.2.1.2. Pesquisa A pesquisa numa rvore balanceada tem eficincia O(log n), dado que o n de testes a realizar igual ao nvel em que foi encontrado o valor passado como parmetro. O critrio de pesquisa : - Caso a rvore a pesquisar esteja vazia, retorna-se o iterador end(); - Caso o valor procurado seja maior que o da raiz, continua-se a pesquisa na subrvore direita; - Menor, esquerda - Igual, retorna-se o iterador para a raiz Mtodo pblico iterator find (const T &e) {return find(root, e) ; }

Mtodo reursivo privado NodePtr find(NodePtr r, const T &e) { if(r==NULL) return end(); if(r->value < e) return find(r->right, e); if(r->value > e) return find(r->left, e); return r; } No entanto, dado que se trata de um mtodo recursivo terminal, facilmente convertvel forma iterativa, com melhor desempenho e sem precisar do mtodo auxiliar: iterator find(const T &e) { NodePtr r=root; while(r!=NULL) { if(r->value < e) r=r->right; else if(e<r->value) r=r->left; else eturn r; } return end(); } No caso de existirem repeties, conveniente retornar o iterador para o elemento mais antigo. Por esse facto iniciamos um apontador com dummy ecomeando pela root executa-se: - caso o valor do n visitado seja menor que o valor procurado, prossegue-se a iterao na subrvore direita - caso contrrio, afecta-se aux com o apontador para esse n e prossegue-se a iterao na subrvore esquerda - atingindo uma folha, testa-se o valor de auxiliar: - caso aux seja dummy ou se o valor procuardo for menor que o valor apontado por aux, conclui-se pela sua no existncia e retorna-se o iterador para o past the end - caso contrrio, retorna-se o iterador par o n apontado por aux. Fazer com estas regras.

5.2.1.4. Insero
O mtodo pblico insertUni() deve retornar 2 valores: um bool a indicar se ocorreu ou no insero e um iterador a apontar para o n da rvore em que o elemento reside, quer tenha sido inserido, quer j existisse anteriormente. O modo normal como a biblioteca standard resolve o caso de um mtodo ter de retornar 2 valores, consiste em usar um template de estruturas pair, instanciar dele uma estrutura template pair com os parmetros adequados (do tipo dos valores a retornar) e declarar a funo retornando uma instncia dessa estrutura. template<class U, class V> struct pair { U first; V second; pair(): first(U()), second(V()) {} pair(const U &u, const V &v) : first(u), second(v) {} }; ex: Comea-se por comparar e com a raiz. Se menor que a raiz compara-se com raiz da subrvore esquerda, se maior, com a direita, se maior direita, se vazia, isere-se; e afecto o membro right do n com o endereo do novo n. pair<iterator, bool> insertUni(const T &e) { NodePtr r; //Apontador para o novo n if root(==NULL) //rvore vazia r=root=dummy->left=dummy->right=newNode(dummy, e); //inicia root com parent=dummy; left e right=NULL else { r=root; for(;;) if (e < r->value) if(r->left != NULL) r=r->left; else {r=insertLeft(r,e); break; } else if (r->value < e) if (r->right != NULL) r=r->right;

else {r=insertRight(r,e); break; } else return pair<iterator, bool>(r,false); } ++sz; return pair<iterator, bool>(r, true); } Os mtodos auxiliares insertLeft() e insertRight() realizam a insero, afectando o membro left ou right do n passado como parmetro com o endereo de um novo n. Providenciam a actualizao de dummy->left ou dummy->right se a insero for realizada num dos extremos da rvore e retornam o apontador para o novo n. Fazer com estas regras.

Mtodo InsertMulti
Ex: comea-se por comparar 6 com a raiz. dado que 6 igual a 6, compara-se com 8 na subrvore direita, seguidamente com 7 e ao tentar comparar com a raiz da subrvore esquerda do n 7, constata-se que esta subrvore est vazia. Ento insere-se como descendente esquerdo do 7. Fazer com estas regras.

5.2.1.5. Remoo
H 2 mtodos: um para remover objectos dado o valor e outro dado um iterador para o n onde esse objecto se encontra. erase(T &e) remove todos os objectos com valor igual a e retornando o n de objectos removidos. Comea por invocar o mtodo find(), que retorna um iterador para o 1 n a remover (caso exista) ou o iterador end() caso no exista. Se existir invoca repetidamente o mtodo erase(i), incrementando o iterador, enquanto o valor apontado pelo iterador for igual ao valor a remover (ordenada por valores ???) template<class T, class A> TreeBase<T,A>size_type TreeBase<T,A>::erase(const T &e) { size_type count = 0; iterator p = find(e); if (p != end()) do {++count; erase(p++); } while (p!=end() && !(e<*p)); return count; } O mtodo erase(iterator pos) providencia a remoo do n referenciado por pos, tendo o cuidado de reconstituir as ligaes dos ns envolventes por forma a que continuem a satisfazer os requisitos de ordenao da rvore. Funo relativa do n a remover, podemos distinguir 3 casos: - O n a remover uma folha - O n a remover tem um nico descendente - O n a remover tem 2 descendentes No 1 caso desliga-se simplesmente o n, afectando com NULL o apontador left ou right do seu parent. (e o left ou right do dummy???) No 2 caso, afecta-se o apontador left ou right do seu parent para passar a apontar para o seu descendente e actualiza-se o membro parent do seu descendente. Os mtodos auxiliares transferParent e setParent tratam do caso (ver pg.306 e 307) No 3 caso, o modo de restabelecer a coerncia exige uma actuao mais complexa: - Identificar o n mais direita da subrvore esquerda (apontado por previous no cdigo) o maior do menores. - desliga-se esse n da rvore, colocando a sua subrvore esquerda (que pode ser vazia) como descendente do seu pai - Insere-se esse n na posio que se situava o n a remover. Resumindo: as aces a realizar pelo mtodo erase() so: - Providenciar a actualizao dos membvros left e/ou right do n dummy, caso o n a remover corresponda ao menor (left most) e/ou ao maior (right most) da rvore global - Desligar o n, tendo o cuidado de reconstituir as ligaes dos ns envolventes por forma a que conmtinuem a satisfazer os requisitos de ordenao da rvore - Libertar a memria ocupada pelo n.

template<class T, class A> void TreeBase<T,A>::erase(iterator i) { NodePtr r = i.current; if (r == dummy.left) //Actualizar left most dummy->left=(++iterator(i)).current; if(r == dummy->right) dummy->right=(--iterator(i)).current; if (r->left == NULL) transferParent(r, r->right); else if (r->right == NULL) transferParent(r, r->left); else { NodePtr previous = rightMost(r->left); //Procurar transferParent(previous, previous->left); //Desligar transferNode(r, previous); //substituir } --sz; deleteNode(r); //Libertar memria } 5.2.1.6/7/8/9 Construtor por Cpia / Operador afectao por cpia / Mtodos auxiliares newNode() e deleteNode() / Destrutor 5.2.1.10. Balanceamento J realmos a necessidade de manter a rvore balanceada (repetidas inseres e remoes desbalanceiam), sob pena de degradar drasticamente o desempenho dos seus mtodos.. O mtodo que vamos analisar permite regenerar o balanceamento de uma rvore, com desempenho pouco eficiente O(n), facto que no o torna recomendvel para aplicaes genricas. A soluo para garantir a permanncia do balanceamento das rvores, frente a repetidas inseres e remoes, sem penalizaes gravosas de desempenho, ser estudada mais adiante, nas rvores red-black. No entanto didctico e elegante pois manipulamos s apontadores, sem necessidade de realizar cpias. - Converte a rvore numa lista ordenada simplesmente ligada, invocando o mtodo auxiliar treeToList() e, seguidamente, converte a lista numa rvore balanceada, invocando listToTree(). template<class T, class a> void TreeBase<T,A>::balance() { if (size() <=2) return; NodePtr header = NULL; treeToList(root, header); root=listToTree(header, size()); root->parent = dummy; } Converso de uma rvore em lista este mtodo providencia a concatenao de todos os ns da rvore numa lista ordenada, simplesmente ligada, usando o campo right dos ns da rvore como apontador next da lista. O algoritmo recursivo : - Se a rvore estiver vazia, terminar o algoritmo; - Converter em lista a subrvore direita; - Inserir o n raiz cabea da lista produzida; - Converter em lista a subrvore esquerda (ficando antes da j produzida). template<class T, classA> void TreeBase<T,A>:: treeToList(NodePtr r, NodePtr &header) { if(!r) return; treeToList(r->right, header); r->right = header; header = r; treeToList(r->left, header); }

Converso em rvore de n Elementos de uma Lista - Se o n de ns da lista for zero, a rvore vazia e termina o algoritmo - Converter em rvore a primeira metade dos ns; - Desligar o n que ficou situado cabea da lista, que passar a constituir a raiz da rvore - Agregar raiz como subrvore esquerda a rvore j obtida - Converter em rvore os restantes ns da lista (n de ns da lista original menos metade menos um) e agregando-a raiz como subrvore direita. ... 5.3. rvores Binrias Organizadas em Heap Heap (monte) toma diferentes significados em informtica. No presente contexto queremos referir a estrutura de dados representada como uma rvore binria completa, tal que, qualquer dos seus ns toma valor maior ou igual ao dos seus filhos, garantindo por esse facto que o n de maior valor a raiz. 5.3.1. Estrutura Heap Tem propriedades muito interessantes que a recomendam para suporte do adaptador dos contentores standard priority_queue e como base conceptual do algoritmo de ordenao heap_sort, aplicvel a contentores sequenciais com acesso aleatrio. Dado ser completa tem representao implcita em array (bloco contguo de memria). 5.3.1.1. Representao de rvores Completas em Array Numeramos os ns de uma rvore completa de cima para baixo e da esquerda para a direita, atribuindo raiz o n 0, a rvore tem uma representao implcita em array, inserindo cada um dos seus ns no ndice correspondente numerao que lhe foi atribuda. Desta correspondncia resulta uma relao aritmtica simples entre os ndices ocupados pelos ns da rvore e os ndices ocupados pelos seus filhos esquerdo e direito, Nomeadamente: - Um n situado no ndice k tem o seu filho esquerdo situado no ndice 2*k+1 e os eu filho direito no 2*k+2 - Os filhos esquerdos situam-se nos ndices mpares e os direitos nops pares - Se um filho esquerdo(direito) estiver no ndice k, o seu irmo direito(esquerdo) est no k+1(k-1) - O pai de um n situado no ndice k situa-se em (k-1)/2 As estruturas de dados representados em rvores completas podem ser alojadas em array ou em qualquer contentor sequencial, cujo iterador seja de acesso aleatrio, como o caso do vector e deque. Uma das consequncias importantes que advm do facto de uma estrutura heap ser representada implicitamente num array, no ser preciso que na sua implementao os ns da rvore disponham dos apontadores left, right e parent como na ABP. O n, neste caso, exclusivamente constitudo pelo objecto T (valor). 5.3.1.2. Algoritmos genricos standard das estruturas heap As setruturas heap revelam-se muito interessantes quendo se trate de aplicaes que se pretenda, atravs de aces push_heap, inserir num contentor sequencial valores aleatrios e poder retirar desse contentor, atravs de uma aco pop_heap(), o elemento de maior valor, ou de menor valor, conforme o critrio de comparao utilizado. Nas estruturas heap consegu-se isso com complexidade O(log n), como s as rvores balanceadas conseguem.

5.4 Adaptador Sequencial priority_queue Um adaptador de contentores sequenciais consiste num template de classes contentoras, que toma como parmetros tipo no s o tipo de objectos que vai alojar, como tambm o tipo de contentor sequencial em que se suporta. Da biblioteca STL de componentes constam 3 tipos de adaptadores de contentores sequenciais: - stack (pilha) - queue (fila de espera) - priority_queue (fila de espera com prioridades) Os primeiros 2 tipos no utilizam estruturas em rvore e as suas definies, para alm dos conceitos que os 3 partilham, resumem-se a uma aplicao dos temas j tratados no captulo anterior. O 3 baseia-se numa estrutura em heap, e interessante p-lo em confronto com os outros 2 para podermos inferir as aplicaes mais adequadas a cada. 5.4.1. Adaptadores de Contentores Caracterizam-se por disponibilizar um n muito reduzido de mtodos pblicos, correspondentes s aces que lhe so tpicas.

5.4.1.1. Class stack<T,Sequence>


Obedece estritamente a uma disciplina FILO e disponibiliza: - push() para inserir objectos no topo da pilha - pop() para retirar/remover objectos do topo da pilha - top() retorna uma referncia para o objecto situado no topo da pilha O parmetro tipo ou segundo argumento do template pode ser o vector, deque ou list, j que todos dispem, na sua interfgace, mtodos que do suporte directo s aces que so especficas dos contentores sequenciais, nomeadamente: - push_back() acrescenta o elemento indicado como argumento, que do tipo Sequence::value_type, no fim do contentor sequencial. - pop_back() no toma argumentos e retira o elemento armazenado no fim do contentor. - back() retorna uma referncia para o elemento que se encontra no fim do contentor. O parmetro tipo Sequence deve ser escolhido criteriosamente, conforme a aplicao a que se destina o stack, tendo em conta que: - O vector pode aumentar o espao reservado, mas no permite reduzi-lo em run-time - O deque permite aumentar e reduzir em run-time e as aces so um pouco menos eficientes que as do vector - A list reserva e devolve espao em run-time, mas apesar de terem complexidade constante nos mtodos pretendidos, so mais lentos que os anteriores. A diferena bsica dos contentores sequenciais e dos adaptadores que os 1s implementam eles prprios a estrutura de dados que lhe especfica, enquanto os adaptadores toma como parmetro o tipo de contentor e agregam como atributo um objecto desse tipo ao qual delegam as aces de acesso. template<class T, class Sequence = deque<T>> class stack { public: typedef typename Sequence::value_type value_type; typedef typename Sequence::size_type size_type; typedef typename Sequence::reference reference; typedef typename Sequence::const_reference const_reference; typedef Sequence container_type; //<<Atributo>> protected: Sequence c; //<<Interface Pblica>> public: stack() : c() {} explicit stack(const Sequence &s) : c(s) {} bool empty() const {return c.empty();}

size_type size() const {return c.size();} reference top() {return c.back();} void push(const value_type &x) {c.push_back(x);} void pop() {c.pop_back();} friend bool operator==(const stack &x, const stack &y) {return x.c==y.c;} };

5.4.1.2. Classe queue<T,

Sequence>

Este adaptador obedece disciplina FIFO e disponibiliza: push() Insere objectos na fila de espera; pop() Remove objectos na cabea da fila; front() Retorna uma referncia para o objecto que permanece mais tempo na fila back() Retorna uma referncia para o objecto que foi mais recentemente inserido. Estas aces podem ser delegadas num contentor sequencial que disponha de um mtodo pop_front() que d suporte ao pop, o que exclui o vector, dado que este no permite remoes no incio do domnio em tempo constante. A definio semelhante, pelo que deixamos como exerccio.

5.4.2. Definio do template priority_queue


Pode entender-se como um refinamento do queue e dispe: push() Insere objectos na fila de espera; pop() Remove o objecto ao qual foi atribudo maior prioridade atravs do objecto funo tipo Cmp que lhe seja passado como 3 parmetro template. top() Retorna uma referncia para o objecto de maior prioridade. O pequeno pormenor que distingue o adaptador priority_queue do adaptador queue o facto do standard impor complexidade O(log n) aos algoritmos de todos os seus mtodos, o que implica que a representao do contentor sequencial em que se suporte seja organizado segundo uma disciplina heap. Este adaptador suporta-se, por omisso, num vector, e os seus mtodos invocam os algoritmos push_heap() e pop_heap() j apresentados. template<class T, class Sequence = vector<T>, class Cmp=less<typename Sequence::value_type>> class priority_queue { public: typedef typename Sequence::value_type value_type; typedef typename Sequence::size_type size_type; typedef typename Sequence::reference reference; typedef typename Sequence::const_reference const_reference; typedef Sequence container_type; //<<Atributos>> protected: Sequence c; Cmp cmp; //<<Interface Pblica>> public: explicit priority_queue(const Cmp &cp = Cmp(), const Sequence &pq=Sequence()) : cmp(cp), c(pq) {} bool empty() const {return c.empty();} size_type size() const {return c.size();} const value_type &top() const {return c.front();} void pop() {pop_heap(c.begin(), c.end(), cmp); c.pop_back(); } void push(const value_type &x) {c.push_back(x); push_heap(c.begin(), c.end(), cmp);} };

CAP.6 RVORES BALANCEADAS


6.1. Introduo As rvores binrias ordenadas por valor de chave de pesquisa so muito boas quanto ao desempenho das aces de pesquisa, remoo e insero, mas exigem que se mantenham balanceadas, isto , que a sua altura se mantenha da ordem do logaritmo do nmero de elementos que contm. Assim, temos que adoptar mtodos de insero e remoo que preservem os eu balanceamento. Em casos especficos, alternativamente, podemos manter controlo sobre a altura e sempre que esta exceda um determinado valor (como sugere Knuth) 5.log2n, balancear com um mtodo como o que se apresentou no cap. anterior, que exige tempo de execuo linear O(n). Em 1962 foram apresentadas as AVL que so ABP que garantem insero e remoo com permanncia de balanceamento. Em 1970 R.Bayer desenvolveu estrutura arborescente de pginas, com elevado n de chaves de pesquisa por pgina. Multidescendentes, portanto e garantem insero e remoo com manuteno de balanceamento. So as B-Tree ou rvores de Bayer. Depois desenvolveu uma variante symmetric binary B-Tree, que B-Tree de ordem 3 (com 2 ou 3 descendentes por pgina) vocacionada para residir em memria central. Estas evoluram para ordem 4 (2, 3 ou 4 descendentes), denominadas red-black. A generalidade das implementaes da biblioteca standard do C++ adoptam as rvores red-black como suporte dos contentores associativos. Os algoritmos das rvores red-black, relativamente s ABP, s diferem quanto aos mtodos de insero e remoo. No entanto, dada a complexidade relativa, quer da implementao, quer da anlise destes 2 mtodos, vamos adoptar uma abordagem a 3 nveis: grfica, pseudo-cdigo e C++ importante pedagogicamente pois o mtodo que deve ser abordado quando algoritmos so muito complexos (mais de 7 decises segundo psiclogos). 6.2. Estruturas B-Tree (rvores de Bayer) Desde os primrdios da informtica que o objectivo de aceder com eficincia a bases de dados de grandes dimenses, com natureza persistente, ocupa lugar de destaque. dado o tempo de latncia, impe-se que os acessos a ficheiro no se faam individualmente por item, como nas rvores binrias, mas sim por pginas (com dezenas ou centenas de itens). Numa ABP mesmo que equilibrada, envolvendo um milho de itens, uma aco de pesquisa pode requerer o teste de 20 ns (O(log2 n). Se aglomerarmos os itens em pginas de 100 itens, o n mximo de acessos a pginas, para a mesma quantidade de itens, apenas 3. Num regime de inseres e remoes frequentes, torna-se impossvel garantir que todas as pginas tenham o mesmo n de itens inseridos. O balanceamento refere-se a pginas. Torna-se necessrio ento providenciar um n mnimo e mximo de itens por pgina, at por causa do espao ocupado em disco. A ordem de uma B-Tree o n mximo de descendentes de cada pgina. Uma B-Tree de ordem N satisfaz: - Todas as pginas tm no mximo N descendentes - Todas as pginas, excepto a raiz, tm no mnimo N/2 descendentes - A raiz tem no mnimo 2 descendentes ( a menos que tambm seja folha) - As folhas situam-se todas ao mesmo nvel e no tm descendentes - Uma pgina que no seja folha, com k descendentes, contm k-1 itens - Numa B-Tree de ordem N, o n de itens contidos situam-se entre N-1 e (N-1)/2 inclusive. item corporizado por uma estrutura contendo um membro com a chave de pesquisa e um membro com o valor associado chave. Os valores inteiros aparecem por ordem crescente da esquerda para a direita, se imaginamos a BTree comprimida num nico nvel. 6.2.1.1. Algoritmo de Pesquisa Vamos considerar cada um dos valores inseridos na B-Tree como raiz de uma rvore, com uma subrvore esquerda, onde se situam valores inferiores a essa raiz, e com uma subrvore direita onde se situam valores que lhe so superiores.

A pesquisa de um valor numa B-Tree processa-se de modo semelhante pesquisa em ABP: - Pesquisa-se primeiro dentro da pgina raiz - Caso o valor procurado no se encontre nessa pgina, prossegue-se a pesquisa na subrvore direita do maior dos menores valores relativamente ao valor procurado - Se o valor procurado for menor que todos os valores existentes na pgina, prossegue-se a pesquisa na subrvore esquerda do menor dos valores existentes nessa pgina. - Termina com insucesso quando se atinge uma pgina folha que no contenha o n. Como as B-Tree so por natureza balanceadas, se providenciarmos dentro de cada pgina, uma procura tambm O(log n), ficamos com um total de eficincia de O(log n). O algoritmo de pesquisa, recursivamente, : - Caso a B-Tree esteja vazia retorna false - Se a pgina raiz contm t, retorna true - Caso contrrio: - Se o valor procurado for menor que todos os valores existentes na pgina, pesquisar recursivamente na subrvore esquerda do menor dos valores existentes nessa pgina; caso contrrio, pesquisar recursivamente na subrvore direita do maior dos valores menores que t nela existentes. 6.2.1.2. Algoritmo de Insero Supondo, para j, que no so permitidos valores repetidos. Para inserir procede-se primeiro sua pesquisa. Caso encontrado, desiste-se da insero e retorna-se informao desse facto. Caso contrrio, insere-se ordenadamente esse valor na pgina folha onde terminou a pesquisa. Se no ficar sobrecarregada, a insero fica consumada. Caso contrrio, teremos que realizar um operao de split, criando uma nova pgina irm sua direita com o seguinte critrio: - O valor central da folha sobrecarregada inserido na pgina ascendente - Os valores que se encontravam direita do valor central transferem-se para uma pgina irm criada sua direita. O split pode ser propagado, no pior dos casos at raiz. As inseres envolvem sempre um par constitudo pelo valor a inserir e pelo apontador para a subrvore direita que lhe fica associada ( a nova criada por split): ex: insert(Pair(7,NULL) --- quando no sabemos onde vai ficar insert(Pair(6,p6)) --- quando h propagao de split. H ainda que ver que h mudana de pgina ascendente (parent) em pginas no directamente envolvidas na insero. Se todas as pginas at raiz tiverem que ser desmembradas, a rvore cresce em altura, mantendo no entanto o balanceamento. A criao da pgina raiz pois a nica circunstncia que pode fazer aumentar a altura da rvore. Cresce das folhas para a raiz ao contrrio das ABP. A insero de valores por ordem crescente, que tornavam a ABP numa lista, aqui no se verifica. Recursivamente, o algoritmo de insero : - Caso a B-Tree esteja vazia (o apontador root igual a NULL), cria a pgina raiz com o elemento a inserir - Caso contrrio, promove a sua insero ordenada na pgina folha. Se a folha ficar sobrecarregada, realiza split dessa folha e invoca recursivamente o algoritmo de insero sobre a pgina ascendente.

CAPTULO 7 CONTENTORES ASSOCIATIVOS


TEORIA
O standard ANSI/ISO estabelece para os contentores associativos que estes devem garantir complexidade O(log n) para os mtodos de pesquisa, insero e remoo (ao contrrio dos contentores sequenciais). Assim, tm de ser suportados em rvores binrias balanceadas (ou quase balanceadas). Os contentores associativos standard que vamos estudar suportam-se no template de classes RBTree, derivando desse template. As tabelas de hash, por vezes, so uma boa alternativa relativamente s rvores, como estruturas de suporte dos contentores associativos, pelo que vamos tambm definir essa extenso biblioteca standard. As tabelas de hash so de utilizao recomendvel, por exemplo, nos casos em que o objectivo a atingir no reduzir o n de colises de chaves ao mnimo, mas sim repartir por vrios contentores parciais os elementos a que se pretende aceder com eficincia. Mritos e demritos dos contentores sequenciais: VECTOR M Suporta iteradores de acesso aleatrio; Complexidade O(1), constante, para insero e remoo no fim do contentor. D Complexidade linear para inseres e emoes no meio e incio do domnio; Limitaes quanto evoluo dinmica: podem aumentar o espao reservado mas no diminui posteriormente. DEQUE M Suportam iteradores de acesso aleatrio (menos eficientes que os de vector); Complexidade O(1) para inseres e remoes em ambos os extremos; Podem aumentar e diminuir o espao reservado ao longo da evoluo dinmica. D Complexidade linear O(n) para inseres e remoes no meio do domnio. LIST M Complexidade O(1) para inseres e remoes em qualquer ponto. D Suportam apenas iteradores bidireccionais; Complexidade linear na pesquisa; Para estruturas de grandes dimenses no tolervel a complexidade O(n) para aces que sejam frequentemente invocadas. A biblioteca STL define 4 variantes de contentores associativos: MAP contentor de elementos constitudos por pares (chave, dados), ordenados por chave, sem repeties; MULTIMAP map que aceita repeties de chaves equivalentes; SET contentor de elementos constitudos apenas pela chave, sem repeties; MULTISET set que aceita mltiplas chaves equivalentes. So denominados associativos porque associam chaves a dados.

INTERFACE PBLICA
begin () Retorna um iterator ou const_iterator para o primeiro elemento. end() Retorna um iterator ou const_iterator para o past the end. swap(container) Trocar os elementos com o contentor indicado clear()

Remover todos os elementos size() Retorna o n de elementos max_size() Retorna a dimenso mxima que pode atingri empty() Retorna true se o contentor estiver vazio // Mtodos adicionais da interface pblica: key_comp() Retorna o objecto funo comparao de chaves usado na construo value_comp() Retorna o objecto funo de comparao de valores usado na construo. insert(t) Insere o valor t. Retorna o pair<iterator,bool>, em que o primeiro membro aponta para um elemento com chave equivalente de t ou para o elemento inserido, conforme o segundo seja false ou true. insert(p,t) Idntico ao anterior, em que o iterador p indica onde deve comear a pesquisa. insert(i,j) Insere os elementos situados no domnio [i,,j[ definido pelos iteradores i e j. erase(k) Remove elementos com chave k e retorna o n de elementos removidos erase(q) Remove elemento apontado por q find(k) Retorna iterador para elemento com chave equivalente a k, ou para o past the end. count(k) Retorna n de elementos com chave k lower_bound(k) ; upper_bound(k) ; equal_range(k) Conjunto de tipos dependentes do objecto funo comparao Cmp, que os contentores devem definir: key_type - tipo das chaves key_compare tipo da comparao usada para ordenar chaves value_compare tipo da comparao usada para valores. Construtor recebem como argumentos um objecto Cmp, que estabelece o modo de comparao dos elementos, e um allocator. Por omisso dos argumentos usado o objecto funo Cmp e allocator A, construdos com o construtor sem parmetros dos tipos indicados nos parmetros template.

TEMPLATE DE CLASSES MAP


template <class K, class T> struct FirstOfPair { const K &operator() (const pair<K,T> &p) const {return p.first;} }; #define SUPER RBTree<K, pair<K,T>, FirstOfPair<K,T>, Cmp, A> template <class K, class T, class Cmp = less<K>, class A = allocator <pair<K,T>> class map: public SUPER { public: IMPORT_TYPE(value_type); IMPORT_TYPE(iterator); typedef T mapped_type; map(const Cmp &c = Cmp(), cons A &a = A()) : SUPER(c,a) {} pair<iterator,bool> insert(const value_type &value) {return insertUni(value); }

T &operator[](const K &key) {return insert(value_type(key, T())).first->second; } }; #undef SUPER EXEMPLO DE COMO SE INSTANCIA E USA O TEMPLATE DE CLASSES map void main() { typedef map<string, unsigned> Table; Table table; string word; while (cin >> word) ++table[word]; table::iterator i; for (i=table.begin(); i != table.end(); ++i) cout << i->first << << i->second << endl; }

TEMPLATE DE CLASSES SET


muito parecido com o map. Tentar fazer. set<K,Cmp,A>

TEMPLATE DE CLASSES MULTIMAP E MULTISET


A diferena que o mtodo acrescentado, RBTree, insert() delega no mtodo da RBTree insertMulti() da RBTree. Tentar fazer.

EXEMPLO DE APLICAO DE UM MULTIMAP


Mostra no console output as palavras lidas do console input, identificando as linhas e colunas em que ocorreram. void main() { typedef string Key; typedef pair<unsigned, unsigned> Position; typedef pair<Key, Position> Value; typedef multimap<Key, Position> Table; typedef Table::iterator Iterator; table table; string line; Key word; unsigned column; for (unsigned numLine=1; getline(cin, line); ++numLine) { //Stream de entrada associado a uma string C-style. istrstream is(line.c_str()); while (is >> word) { column=is.telg() word.size() + 1; table.insert(Value(word, Position(numLine, column))); } } for (iterator i=table.begin(); i != table.end(); ++i) cout << i->first << [ << i->second.first << : << i->second.second << ]<< endl; } Notas sobre este programa: Ao extrair uma palavra de um istream (cin) no se consegue distinguir a linha em que estava, dado que o carcter \n consumido como separador de palavras. A soluo proposta ler uma linha, invocando a funo global getline() e posteriormente instanciar um istrstream passando ao seu construtor, como parmetro, a cadeia de caracteres lida.

TABELAS HASH (abertas)


TEORIA As tabelas de hash adoptam o critrio de converter a chave de pesquisa, por uma relao aritmtica, directamente no ndice de um array onde se encontra o valor a ela associado. Em casos favorveis consegue-se encontrar em tempo constante o valor pesquisado, ou seja, envolvendo um nico teste. A correspondncia entre o universo das chaves e o universo dos ndices deve ser unvoca mas normalmente no biunvoca. Assim, as colises so normais, isto , associado a cada ndice do array a, no corresponde exclusivamente uma chave, mas sim uma lista de chaves, associadas aos respectivos dados. A pesquisa dos dados associados a uma chave exigir, alm da execuo da funo h(k), um acesso ao array a indexado por h(k) e o teste de comparao para confirmar se de facto a[h(k)] corresponde chave procurada. 3 casos pode ocorrer: a[h(k)] um apontador NULL ==> chave k no consta da tabela. a[h(k)] aponta par um n da lista da qual consta a chave k ==> pesquisa com um s teste a[h(k)] aponta para um n da lista que no corresponde chave pesquisada ==> testes sequenciais ao longo da lista - O(n). O factor de carga M/N. Quanto menor for o factor de carga, menor a probabilidade de colises, como convm (mas a relao no linear). Caso as chaves de pesquisa sejam strings deve inicialmente providenciar-se a converso da string num valor numrico e s depois efectuar a converso desse valor em ndices do array. Baseados numa estimativa do n mximo de chaves a inserir na tabela, implementa-se um array de apontadores para listas simplesmente ligadas. Dos ns das listas constam pares (chave, valor) (mais apontador para next). A cada conjunto de chaves em coliso chama-se bucket. Deseja-se pois que os buckets no sejam muito grandes pois no interior deles que se tem de pesquisar (sequencialmente) no caso de colises de chaves. FUNES HASH Condies bsicas: - Envolver operaes aritmticas de rpida execuo - Minimizar o n de colises Das caractersticas da funo hash depende a eficincia dos mtodos de insero, remoo e pesquisa. Deve pois apresentar probabilidade uniforme para todos os elementos do contradomnio, mas isso tambm depende da distribuio das chaves de domnio. No h funo hash ptima para todas as aplicaes. endereamento directo domnio dos valores de chaves = domnio dos ndices do array. funo hash perfeita, porque biunvoca. No sempre possvel devido ao desperdcio de memria. O critrio que preside ao estabelecimento de uma funo hash procurar uma relao entre a chave de pesquisa e um valor numrico, mdulo size_t, o mais disperso possvel. Ento melhor que N seja sempre n primo. CRITRIOS DE OPTIMIZAO Dimenso do array: - Deve ser n primo - Ser maior que o n de chaves que previsvel inserir (factor de carga < 1) - Se pretendido tabela dinmica, tem de estar-se sempre a verificar factor de carga e, quando se aproximar de 1, aumentar o tamanho do array para o n primo que seja o mais prximo do dobro do anterior. Esta operao morosa mas imprescindvel. Para poupar tempo interessa que na definio da tabela conste como constante um array de nmeros primos previamente calculados, organizado por ordem crescente com progresso geomtrica de razo 2, atravs da qual a dimenso real a adoptar para a tabela de hash se faa por mera consulta a esse array. Usa-se o crivo de Eratosthenes. Pode-se ento construir uma classe Prime, que contm como atributo o ndice i do array, e com os seguintes mtodos: Prime(n) construtor invocado com o valor estimado para a dimenso da tabela afecta i com o ndice do nmero primo aproximado por excesso. set(n) Afectai com o ndice do n primo aproximado por excesso a n

operator prime_type operador de converso para prime_type que retorna o n primo correspondente ao ndice i next() incrementa i e retorna o n primo correspondente. get() auxiliar para retornar o n primo correspondente ao ndice i FUNO hash_string ...

TEMPLATE DE FUNES HASH


Do template de tabelas hash consta como parmetro-tipo a classe template objecto funo hash que se pretenda adoptar, tomando por omisso a classe template de uso genrioco mostrada a seguir: template <class Key> struct Hash { size_t operator() (unsigned long x) const {return size_t(x); } }; Este template de classes objecto funo toma como parmetro o tipo da chave, tem uma verso bsica para os tipos integrais (convertveis em unsigned long) que se limita a retornar o valor do parmetro e especializaes para tipo char* e string. Na especializao para string C-style ou string, o operador chamada a funo pe em execuo a funo hash_string() de uso genrico (por exemplo, uma das que se estudaram anteriormente) template<> struct Hash<char*> { size_t operator() (const char *s) const {return hash_string(s); } }; template<> struct Hash<string> { size_t operator() (const string &s) const {return hash_string(s.c_str());} };

TEMPLATE DE CLASSES HashTable


---> Parmetros tipo: K a chave de pesquisa FH a classe funo de hash. Como valor por omisso instanciada a classe template Hash<K> V a classe dos valores KFromV um tipo de objecto funo que infere K a partir de V Equal um tipo objecto funo que estabelece o critrio de equivalncia das chaves de pesquisa A o tipo de allocator adoptado #define SUPER Container<V,A> template<class K, class FH=Hash<K>, class V=K, class KFromV = Identity>K,V>, class Equal = equal_to<K>, class A = allocator<V> > class HashTable:public SUPER { public: //<<Tipos>> IMPORT_TYPE(pointer); IMPORT_TYPE(const_pointer); IMPORT_TYPE(reference); IMPORT_TYPE(const_reference); IMPORT_TYPE(size_type) IMPORT_TYPE(allocator_type); typedef K key_type; typedef Equal key_compare; typedef FH hasher;

A estrutura Node (n) definida nested e privada de HashTable tem um atributo next, do tipo apontador para Node, e um atributo data do tipo dos objectos de que a tabela de hash contentora. O tipo apontador para Node obtido a partir do tipo pointer do allocator de objectos Node. O tipo do allocator de objectos Node obtido a partir do tipo other do template de classes rebind do allocator A, instanciado para a classe template rebind<Node>. protected: typedef KFromV kfromv_type; struct Node; typedef typename A::rebind<Node>::other Anode; typedef typename ANode::pointer NodePtr; typedef typename Anode::const_pointer ConstNodePtr; struct Node { V data; NodePtr next; }; A tabela de buckets implementada num vector de apontadores para o primeiro n da lista simplesmente ligada (cabea da lista) typedef typename A::rebind<NodePtr>::other AnodePtr; typedef vector<NodePtr, AnodePtr> Table; typedef typename Table::iterator Titerator typedef typename Table::iterator TconstIterator; //<<Iteradores>> template <class P, class R, class TI, class NP< > class IT; template <class P, class R, class TI, class NP< > friend class IT;

//<<Atributos>>
//Apontador para funo ou objecto funo hash FH fh; //Apontador para funo ou objecto funo que verifica se duas chaves so equivalentes Equal equal; //Apontador para funo ou objecto funo que infer K a partir de V KFromV kFromV; //Nmero de buckets da tabela. O n de buckets da tabela ser um n primo Prime prime; A constr; //Allocator para construir objectos v ANode alloc; //Allocator para aloja e desalojar ns size_type sz; //N de elementos contidos na tabela //Vector de estruturas listas simplesmente ligada Table buckets; //<<Mtodos auxiliares>> ... //<<Interface Pblica>> public: typedef IT<pointer, reference, Titerator, NodePtr> iterator //<<Construtores, destrutor e operador afectao>> //O construtor tem por parmetro o n de entradas da tabela e como opcionais: a funo hash, a funo de equivalncia de chaves, a funo para extraco da chave e o allocator. HashTable(size_type n=0, const FH &f=FH(), const Equal &e=equal(), const KFromV &kv=kFromV(), const A &a=A()) : fh(f), equal(e), kFromV(kv), prime(n), constr(a), alloc(a), sz(0), buckets(prime, NodePtr(), a) {}; HashTable(size_t n, const FH &f, const Equal &e, const A &a) : fh(f), equal(e), prime(n), constr(a), alloc(a), sz(0), buckets(prime, NodePtr(), a) {} HashTable(const HashTable&); //Construtor por cpia ~HashTable() {clear(); } //Destrutor HashTable &operator=(const HashTable&); //Afectao //<<Observadores>> hasher hash_funct() const {return fh; }

key_compare key_comp() const {return equal; } allocator_type get_allocator() const {return constr; } //<<Mtodos Pblicos size_t size() const {return sz;} bool empty() {return size()==0;} size_type bucket_count() const {return buckets.size(); } void resize(size_type n) {if (n>prime) {prime.set(n); expand(n); }} //<<Pesquisa, Insero e Remoo>> //Pesquisa por chave. Se existir na tabela um elemento com a chave passada como parmetro retorna um iterador para o elemento ou o past the end caso contrrio. iterator find(const K &k); //Insere um elemento na tabela caso no exista. No membro second retorna true se o elemento no existia e false caso contrrio. No membro first retorna o iterador para o elemento inserido ou para o elemento existente pair<iterator, bool> insertUni(const V&); //Insero de um elemento na tabela iterator insertMulti(const V&); //Remove da tabela todos os objectos cuja chave seja igual passada como parmetro. Retorna o nmero de elementos removidos size_type erase(const K&); void clear(); //Remove todos os ns da tabela //<<Acesso por iterador>> iterator begin() {return iterator(buckets.begin(), buckets.end()); } iterator end() {return iterator(buckets, bucket_count()); }

Implementao dos Mtodos de Pesquisa, Insero e Remoo


Limitam-se a calcular sobre qual dos buckets devem invocar o mtodo auxiliar homnimo e construir o respectivo valor de retorno. O valor retornado por cada um dos mtodos depende da operao que for invocada sobre o contentor. Nas aces de insero e remoo, o contador de n de elementos actualizado, tendo em conta o resultado da operao sobre o bucket. No mtodo find(), o iterador retornado construdo tendo em conta o resultado da pesquisa no bucket e a entrada na tabela correspondente chave: Pesquisa iterator find(const K &k) { size_type i=bucketNum(k); return iterator(buckets, i, find(buckets[i], k); } Por sua vez, o mtodo auxiliar find() num bucket percorre os elementos da lista e termina a iterao quando encontrar um objecto com chave equivalente da pesquisa ou atingir o fim do bucket NodePtr find(NodePtr header, const K &k) { NodePtr cur=header; for (;cur && !equal(k, KFromV(cur->data)); cur=cur->next); return cur; } Insero pair<iterator, bool> insertUni(const V &v) { expand(size()+1); size_type i = bucketNum(kFromV(v)); pair<NodePtr, bool> r = insert<flase>(buckets[i], v); return make_pair(iterator(buckets,i,r.first), r.second); } Tal como na ABP, definiu-se um template de mtodos auxiliares insert() com parmetro-valor do tipo bool que, quando instanciado com o valor true, executa insero mltipla (permite inserir mltiplos elementos com chaves equivalentes) e quando instanciado com o valor false executa exclusivamente inseres simples. template <bool MULTI> pair<NodePtr, bool> insert(NodePtr &header, const V &v) {

NodePtr curr = find(header, kFromV(v)); if(curr) { if (MULTI) { curr->next = newNode(curr->next, v); ++sz; return make_pair(curr->next, true); } return make_pair(curr, false); } ++sz; return make_pair(header = newNode(header, v), true); } O mtodo insertMulti() muito semelhante ao insertUni, s com true. Remoo size_type erase(const K &k) { return erase(buckets[bucketNum(k)], k); Com o mtodo auxiliar erase() explicitado abaixo. As remoes numa lista simplesmente ligada (sem n dummy) implicam, para alm da destruio dos ns a remover, afectar o membro next dos ns anteriores ou o header da lista, com o endereo do n seguinte ao n removido. Isto requer, tal como na insero, que ao longo da iterao de pesquisa seja mantida informao actualizada do apontador para o n anterior ao n corrente: size_type erase(NodePtr &header, const K &key) { NodePtr prev = NULL, curr = header; size_type count = 0; while (curr) if (equal(key, kFromV(curr->data))) { curr = curr->next; if(prev) {deleteNode(prev->next); prev->next=curr; } else {deleteNode(header); header = curr; } ++count; } else if (count > 0) break; else {prev = curr; curr = curr->next; } sz-=count; return count; }

CONTENTORES ASSOCIATIVOS HASH


As definies dos templates de classes HashMap, HashSet, HashMultiMap e HashMultiSet, suportam-se no template de classes HashTable analisado anteriormente, reduzem-se ao seguinte: #define SUPER \ HashTable<K, FH ,pair<K,T>, FirstOfPair<K,T> ,Cmp, A> template <class K, class T, class FH=Hash<K>, class Cmp=equal_to<K>, class A=allocator<pair<K,T>> class HashMap: public SUPER { public: IMPORT_TYPE(iterator); IMPORT_TYPE(value_type); IMPORT_TYPE(size_tyep); typedef T Mapped_type; HashMap(size_type n=0, const FH &h=Fh(), const Cmp &c=Cmp(), const A &a=A()) : SUPER(n,h,c,a) {} pair<iterator,bool> insert(const value_type &value) {return insertUni(value); } T &operator[] (const K &k) {return insert(pair<K,T>(k,T())).first->second; }}; Os outros so semelhantes e podem ser vistos na pgina 521 do livro.

You might also like