list templates vamos considerar a lista ligada (singly linked list) o objecto da classe slink o...

42
List templates considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL next next slink() { next = 0; } slink(slink* p) { next = p; } ct slink { ink* next; ink() { next=0; } ink(slink* p) { next=p; } };

Upload: internet

Post on 17-Apr-2015

107 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

List templatesVamos considerar a lista ligada (singly linked list)

O objecto da classe slink

O objecto da classe slink

O objecto da classe slink

NULLnext

next

slink() { next = 0; }

slink(slink* p) { next = p; }

struct slink { slink* next; slink() { next=0; } slink(slink* p) { next=p; } };

Page 2: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Agora podemos definir a classe slist_base que pode conter osobjectos de qualquer classe derivada de slink:

class slist_base {slink* last; // last->next é a cabeça da lista

public:void insert(slink* a); // incluir na cabeça da listavoid append(slink* a); // incluir no final da listaslink* get(); // retornar e remover a cabeçavoid clear() { last = 0; }slist_base() { last = 0; } // o primeiro construtorslist_base(slink* a) { last = a->next = a; }

// o segundo construtorclass bad_last {}; // a classe de excepçõesfriend class slist_base_iter; // a classe iterador

};

Page 3: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

O nome slist_base significa que a classe vai ser usada como a basepara (classes de listas ligadas) singly linked list classes.

Vamos considerar a implementaçãodas funções diferentes pertencentes à classe slist_base. Elas são

insert, append e get.void slist_base::insert(slink* a){ if(last)

a->next = last->next;else last = a;last->next = a;

}

Page 4: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::insert(slink* a){ if(last)

a->next = last->next;else last = a;last->next = a;

}

last

next

last->next é a cabeça da lista

next

a

last=0

next

a

next

a=last

else last = a;last->next = a;

Page 5: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::insert(slink* a){ if(last)

a->next = last->next;else last = a;last->next = a;

}

last0

a->next = last->next;

last->next = a;

next

a

next

last

next

last

next

Page 6: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::insert(slink* a){ if(last)

a->next = last->next;else last = a;last->next = a;

}

last0

a->next = last->next;

last->next = a;

next

a

next

nextlast

last

next

Page 7: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

O objecto da classe

slink

last->next

last

O objecto da classe

slink

last->next

last

a->next

a

slink

incluir nacabeça dalista

{ if(last) a->next = last->next; else last = a; last->next = a; }

void slist_base::insert(slink* a)

a->next = last->next;

last->next = a;

Page 8: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::append(slink* a){ if(last) {

a->next = last->next;last = last->next = a; }

elselast = a->next = a;

}

Page 9: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::append(slink* a){ if(last) {

a->next = last->next;last = last->next = a; }

elselast = a->next = a;

}

next

a

last=0

next

a

last = a->next = a;

last

Page 10: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::append(slink* a){ if(last) {

a->next = last->next;last = last->next = a; }

elselast = a->next = a;

}

last->next é a cabeça da lista

next

a{a->next = last->next;last = last->next = a; }

last0

last

next

last

last

next

next

last->next

Page 11: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::insert(slink* a)

last

next next

a

void slist_base::append(slink* a)

next next

last

Cabeça

Cabeça Final

Page 12: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

O objecto da classe

slink

last->next

last

O objecto da classe slink

last->next

last

a->next

a

slink

incluir nofinal da

lista

{ if(last) a->next = last->next; last = last->next = a; else last = a->next = a; }

void slist_base::append(slink* a)

a->next = last->next;

last->next = a;

last

last = a;

Page 13: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

O primeiro objecto

last->next

last

a insertO primeiro objecto

a->next = a

last

appenda

last=a

last

newincluir nacabeça

newincluir nofinal

last

lasta a

A seguinte figura mostra como as funções insert e append funcionam.

Page 14: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

slink* slist_base::get(){ if (last == 0) throw bad_last();

slink* f = last->next;if ( f == last)

last = 0;else

last->next = f->next;return f;

}

Page 15: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

last

retornar

slink* slist_base::get(){ if (last == 0) throw bad_last();

slink* f = last->next;if ( f == last) // remover o

// ultimo elementolast = 0;else

last->next = f->next; return f; }

f

Page 16: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Para usar a classe slist_base podemos derivar da classe slink aclasse nova. Por exemplo, vamos considerar o elemento name queprecisamos de incluir na lista:

class name : public slink {. . . . . . . . . . . . . . . . . . . . };void f(const char* s){ slist_base slb;

slb.insert(new name(s) );// . . . . .name* p = (name*)slb.get();// cast explícito// . . . . .delete p;

}

Este estilo não é bom. Nós gostaríamos de fornecer a versão type-safe da classe slist_base:

Page 17: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

template<class T> class Islist : private slist_base {public:

void insert(T* a) { slist_base::insert(a); }void append(T* a) { slist_base::append(a); }T* get() { return (T*) slist_base::get(); }

};

De notar que slist_base é uma classe base privada da classe Islist.Nós não queremos permitir ao utilizador mudar a classe slist_base.

Este template pode ser usado da seguinte forma:void f(const char* s){ Islist< name> ilst;

ilst.insert(new name(s) );// . . . . .name* p = ilst.get();// . . . . .delete p;

}

Page 18: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Neste caso o objecto pode ser incluído na Islist se este objecto forderivado da slink. Por isso, nós não podemos definir Islist,

por exemplo, de inteiros. Vamos considerar uma lista que não requer esta restrição.

template<class T> struct Tlink : public slink {T info;Tlink(const T& a) : info(a) { }

A classe Tlink<T> tem a cópia do objecto do tipo T e ligaçãofornecida através da classe base slink. Agora podemos declarar

a classe Slist.

Page 19: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

template<class T> class Slist_iter; // vamos considerar esta linha // um pouco mais à frente

template<class T> class Slist : private slist_base {public: void insert(const T& a) { slist_base::insert(new Tlink<T>(a) ); } void append(T& a) { slist_base::append(new Tlink<T>(a) ); } T get(); friend class Slist_iter<T>; // vamos considerar esta linha

// um pouco mais à frente };

template<class T> T Slist<T>::get(){ Tlink<T>* lnk = (Tlink<T>*) slist_base::get();

T i = lnk->info;delete lnk;return i;

}

Page 20: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

O uso da classe Slist é tão fácil como o uso da classe Islist. A diferençaé que é possível definir o objecto da classe Slist sem necessidade de o

derivar da respectiva classe slink. Mas intrusive list, tais comoIslist, têm a vantagem na eficiência em termos de execução e

frequentemente em compacidade. Cada vez que o objecto for passado na Slist esta lista precisa de reservar o objecto do tipo Tlink e

copiar o tipo. Como resultado duas coisas podem ser feitas.Primeiro, Tlink é um bom reservador de memória. Segundo,

isto é boa ideia guardar objectos na “primary list” que é intrusive eusar a lista não intrusive apenas quando é necessário o membership

de algumas listas.

void f(name* p){ Islist<name> lst1;

Slist<name*> lst2;lst1.insert(p); // ligação através do objecto ‘*p’lst2.insert(p); // o uso do objecto separado para guardar ‘p’

}

Page 21: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Slist é uma classe boa para pequenos objectos, tais como inteiros eponteiros. Para grandes objectos é melhor guardar ponteiros para

estes objectos na lista.

IteraçãoA classe não fornece qualquer possibilidade para olhar para dentroda lista. Mas ela declara a classe amiga (friend) slist_base_iter. Por

isso, nós podemos declarar o iterador:

Page 22: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

class slist_base_iter {slink* ce; // o elemento correnteslist_base* cs;// a lista corrente

public:inline slist_base_iter(slist_base& s);inline slink* operator()();

};slist_base_iter::slist_base_iter(slist_base& s){ cs = &s;

ce = cs->last; }slink* slist_base_iter::operator()()

// retornar 0 para indicar o final do iteração{ slink* ret = ce ? (ce=ce->next) : 0;

if (ce == cs->last) ce =0;return ret;

}A seguinte figura explica como o construtor da classe slist_base_iter e

o operador operator()() funcionam .

Page 23: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

slist_base_iter::slist_base_iter(slist_base& s){ cs = &s;

ce = cs->last; }slink* slist_base_iter::operator()()

// retornar 0 para indicar o final do iteração{ slink* ret = ce ? (ce=ce->next) : 0;

if (ce == cs->last) ce =0;return ret;

}

last

next

CS

ce cece ce ce

Page 24: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Para um dado iterador todos os iteradores para Slist e Islist podem serconstruídos. Primeiro devemos declarar os iteradores como friends

para as classes deles.

template<class T> class Islist_iter;template<class T> class Islist : private slist_base {public:

friend class Islist_iter<T>;};

template<class T> class Slist_iter;template<class T> class Slist : private slist_base {public:

friend class Slist_iter<T>;};

De notar que os nomes dos iteradores foram introduzidos sem definiras suas classes template. Isto apresenta a possibilidade de usardependências entre templates. Agora vamos definir iteradores.

Page 25: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

template<class T> class Islist_iter : private slist_base_iter {public:

Islist_iter(Islist<T>& s) : slist_base_iter(s) { }inline T* operator()()

{ return (T*) slist_base_iter::operator()(); }};

template<class T> class Slist_iter : private slist_base_iter {public:

Slist_iter(Slist<T>& s) : slist_base_iter(s) { }inline T* operator()();

};template<class T> T* Slist_iter<T>::operator()(){ Tlink<T>* lnk = (Tlink<T>*) slist_base_iter::operator()();

return lnk ? &lnk->info : 0;}

Page 26: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

De notar que mais uma vez nós usamos a derivação duma família declasses (que são os templates) da classe base única. Neste caso a

herança expressa comunalidade e permite eliminar a replicação docódigo.

Os nossos iteradores podem ser usados da seguinte forma:

Page 27: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void main(void){ try {

Slist<int> lst1;lst1.insert(3); lst1.insert(4); lst1.insert(5);int p = lst1.get(); cout << p << endl;p = lst1.get(); cout << p << endl;p = lst1.get(); cout << p << endl;lst1.insert(30); lst1.insert(40); lst1.insert(50);Slist_iter<int> iter1(lst1);const int* ii;while( (ii=iter1() ) != 0 )

cout << (*ii) << endl;}

catch(slist_base::bad_last){ cerr << "bad last\n"; exit(1); }

}

Os resultados:543

504030

Page 28: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Sumário de templates

1. Os templates são uma das capacidades do C++ para a reutilizaçãode código.2. Existem dois tipos de template: class template e function template.É permitido usar templates para as funções globais e para asfunções locais pertencentes a qualquer classe.3. Template permite passar um ou mais tipos dentro da classecomo parâmetros. O tipo pode ser ou qualquer tipo predefinido nalinguagem ou qualquer tipo novo definido pelo utilizador.4. O argumento de function template deve definir o tipo com pelo menos um argumento na função. Isto permite garantir que a própriaversão da função vai ser seleccionada com a ajuda da avaliação dostipos dos argumentos desta função.5. Os parâmetros de template devem ser fornecidos em símbolostais como (< >). Alguns parâmetros dentro de (< >) podem sertambém símbolos < >. Para evitar ambiguidade precisamos deinserir o espaço entre os símbolos respectivos.

SWAP<int,comp<int> >::swap(my_array);

Page 29: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

6. Templates permitem construir os programas que foramcompostos de partes relativamente independentes. Uma parte éorientada na construção do interface e a outra - na realizaçãode funções diferentes. Finalmente esta possibilidade dá novos meiospara suportar o encapsulamento e os tipos abstractos,particularmente para separar o interface e a implementação.7. As regras adoptadas na linguagens C/C++ para converter ostipos dos argumentos não podem ser usadas para templates.8. Function template pode ser redefinida nas seguintes ocasiões:existem outras funções que têm o mesmo nome ou existem outrasfunctions template que têm o mesmo nome.9. Podemos usar as expressões constantes como argumentos detemplate.10. Ao declarar os objectos de qualquer classe definidos com aajuda de templates podemos dizer que dois objectos são objectosda mesma classe se eles tiveram o mesmo template ou todos osargumentos tiverem os mesmos valores.

Page 30: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

11. Dois tipos construídos do mesmo template são diferentesindependentemente da possibilidade das conversões automáticasadoptadas na linguagem C++.12. Os argumentos de template podem ser só expressões constantes(i.e. neste caso eles não são tipos).13. Ao definir os argumentos de template como classes bases ederivadas nós vamos perder muitas conversões definidas nalinguagem para objectos destas classes.14. Existe uma relação entre templates e a herança. Templatespermitem mostrar abstracções comuns entre tipos diferentes. Aherança permite apresentar interfaces comuns para classes diferentes.15. O uso de templates e herança dá-nos os meios para separar ointerface da implementação.16. A declaração de template só pode ser global.17. Cada classe ou função geradas de acordo com o template têmcopias únicas dos seus componentes estáticos.

Page 31: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

Agora vamos considerar alguns exemplos

struct slink

template<class T> struct Tlinkpublic

class slist_base

template<class T> class Slist

private

template<class T> class Islist

private

class slist_base_iter

template<class T> class Islist_iter

template<class T> class Slist_iter

private private

Page 32: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

struct slink { slink* next; slink() { next=0; } slink(slink* p) { next=p; } };

template<class T> struct Tlink : public slink {T info;Tlink(const T& a) : info(a) { }

};

Page 33: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

class slist_base {slink* last; // last->next é a cabeça da lista

public:void insert(slink* a); // incluir na cabeça da listavoid append(slink* a); // incluir no final da listaslink* get(); // retornar e remover a cabeçavoid clear() { last = 0; }slist_base() { last = 0; } // o primeiro construtorslist_base(slink* a) { last = a->next = a; }

// o segundo construtorclass bad_last {}; // a classe de excepçõesfriend class slist_base_iter; // a classe iterador

};

template<class T> class Slist : private slist_base {public: void insert(const T& a) { slist_base::insert(new Tlink<T>(a) ); } void append(T& a) { slist_base::append(new Tlink<T>(a) ); } T get(); friend class Slist_iter<T>;

};

Page 34: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

template<class T> class Islist : private slist_base {public:

void insert(T* a) { slist_base::insert(a); }void append(T* a) { slist_base::append(a); }T* get() { return (T*) slist_base::get(); }friend class Islist_iter < T >;

};

Page 35: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

void slist_base::insert(slink* a){ if(last) a->next = last->next; else

last = a; last->next = a;}

void slist_base::append(slink* a){ if(last) {

a->next = last->next;last = last->next = a; }

elselast = a->next = a;

}

slink* slist_base::get(){ if (last == 0) throw bad_last(); slink* f = last->next; if ( f == last)

last = 0; else

last->next = f->next; return f;}

Page 36: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

template<class T> T Slist<T>::get(){ Tlink<T>* lnk = (Tlink<T>*) slist_base::get();

T i = lnk->info;delete lnk;return i;

}

Forward declarations:template<class T> class Slist_iter; template<class T> class Islist_iter;

Page 37: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

class slist_base_iter {slink* ce; // o elemento correnteslist_base* cs; // a lista corrente

public:inline slist_base_iter(slist_base& s);inline slink* operator()();

};

template<class T> class Islist_iter : private slist_base_iter {public:

Islist_iter(Islist<T>& s) : slist_base_iter(s) { }inline T* operator()()

{ return (T*) slist_base_iter::operator()(); }};

template<class T> class Slist_iter : private slist_base_iter {public:

Slist_iter(Slist<T>& s) : slist_base_iter(s) { }inline T* operator()();

};

Page 38: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

slist_base_iter::slist_base_iter(slist_base& s){ cs = &s;

ce = cs->last; }

slink* slist_base_iter::operator()()// retornar 0 para indicar o final do iteração

{ slink* ret = ce ? (ce=ce->next) : 0;if (ce == cs->last) ce =0;return ret;

}

template<class T> T* Slist_iter<T>::operator()(){ Tlink<T>* lnk = (Tlink<T>*) slist_base_iter::operator()();

return lnk ? &lnk->info : 0;}

Page 39: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

int main(int argc, char* argv[]){ slist_base sb; slink sl; sb.insert(&sl);try {

Slist<int> lst1;lst1.insert(3); lst1.insert(4); lst1.insert(5);int p = lst1.get(); cout << p << endl;p = lst1.get(); cout << p << endl;p = lst1.get(); cout << p << endl;lst1.insert(30); lst1.insert(40); lst1.insert(50);Slist_iter<int> iter1(lst1);const int* ii;while( (ii=iter1() ) != 0 )

cout << (*ii) << endl;}

catch(slist_base::bad_last){ cerr << "bad last\n"; exit(1); }return 0;

}

Page 40: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

size

c_l

template <class T> class stack { unsigned counter;

int size;T* c_l;T* top;

public:T pop();void push(T&);stack(unsigned);virtual ~stack();int get_size() { return size; }int get_counter() { return counter; }

};

top

cou

nter

Page 41: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

template <class T> stack<T>::stack(unsigned tamanho){ top = c_l = new T[size = tamanho];

counter=0; }

template <class T> stack<T>::~stack(){ delete [] c_l; }

template <class T> void stack<T>::push(T &new_s){ *top++ = new_s; counter++; }

template <class T> T stack<T>::pop(){ if(counter==0) { cerr<<"stack is empty\n";

return NULL; }counter--;return *--top;

}

Page 42: List templates Vamos considerar a lista ligada (singly linked list) O objecto da classe slink O objecto da classe slink O objecto da classe slink NULL

int main(int argc, char* argv[]){ stack<char*> pilha=10;

char *ss1 = "Aveiro",*ss2 ="Ilhavo";double a = 10.15, b = 5.45;pilha.push(ss1);pilha.push(ss2);cout << pilha.get_size() << endl;cout << pilha.get_counter() << endl;cout << pilha.pop() << endl;cout << pilha.pop() << endl;stack<double> pilha1(10);pilha1.push(a);pilha1.push(b);cout << pilha1.pop() << endl;cout << pilha1.pop() << endl;return 0;

}