terça-feira, setembro 19, 2006

Não somente, estrutura de dados diversas não somente podem ser implementadas em templates em C++, mas também iteradores de estruturas de dados. ..... responsável por "atravessar" um contêiner em uma determinada ordem e para cada elemento visitado de um contêiner disponibilizado para algum tipo de processamento. A ordem de atravessamento depende do tipo de contêiner. Em uma lista por exemplo pode ser do início para o fim ou vice-versa; em uma árvore pode ser em pré-ordem, pós ordem. Há iterators que em adição permitem remoção e adição de elementos no contêiner. No exemplo a seguir é de um template que implementa um iterador de lista linearmente encadeadas, ....., a ordem dos links, ou seja, da cabeça para a cauda. Um iterador sempre possui métodos que informam se ainda há elementos do contêiner a serem visitados e em caso verdadeiro qual é o próximo elemento a ser visitado. Estas operações são definidas no template pelos métodos HasNext e Next

template<>
class ListIteratorImp
{

private:

ListImp<>& List;
T* Cur;

public:

ListIteratorImp( ListImp<>& List): List( List), Cur(Cist.PeekHead())
{}
bool HasNext() const
{

return Cur!= "NULL";

}
T* Next()
{

T* temp= Cur;
if(Cur)

Cur= Cur->Next;

return temp;

}
void Restart()
{

Cur= List.PeekHead();

}

};

Em C++, templates podem também ser moldes de funções como no exemplo a seguir:

template <>
void Print(ListImp<>& List)
{
for(ListIteratorImp<> Lit(List); Lit.HasNext();)
Print(Lit.Next());
}

O template acima é um gerador de funções sendo que, cada função gerada tomo como argumento uma referência para um lista de objetos do tipo T, parâmetro do template, e que usando um iterador atravessa a lista em invoca um método Print responsável por imprimir objetos do tipo T. Note o uso das funções HasNext e Next. Observe também que ao ser gerada a função para um tipo T, uma função Print que toma como argumento um ponteiro para o tipo T deve ter sido anteriormente. O compilador gera uma função a partir de um template quando esta for chamada em alguma expressão e não existir nenhuma função com o mesmo nome, número e tipo de parâmetros definidos manualmente. O exemplo abaixo ilustra:



void Print(Banana*);
ListImp<> bananas;
void main()
{
bananas.Add(new Banana(...));
...
Iterate(bananas, Print);

}

Podemos definir em uma função mais genérica para processamento de uma lista a qual toma como argumento um ponteiro para uma função a ser chamada para cada elemento visitado por um iterador, assim ao invés de ter uma função para imprimir, outra para salvar em arquivo, etc, teremos apenas uma função que toma como argumento a operação a ser realizada, isto opde ser implementado pelo seguinte template:

template<>
void Iterate(ListImp<>& List, void (*func)(T*))
{

for(ListIteratorImp<>& Lit(List); Lit.HasNext();)
(*func)(Lit.Next());
}

O trecho de código a seguir ilustra o uso do template:...Uma alternativa aos métodos HasNext da classe paramétrica ListIteratorImpm, consiste em sobrecarregar os operadores de conversão para inteiro e de pré e pós incremento. Desta forma um objeto do tipo ListIteratorImp<> pode ser implicitamente convertido para inteiro em expressões de testes em sentenças de iteração, for, do e while, dispensando assim o uso de HasNext. Similarmente o próximo elementoa a ser visitado pelo iterador pode ser obtido pelos operadores de incremento, dispensando o uso de Next, a idéia é tornar o código mais parecido com o de laços envolvendo números e portanto mais legível.(note no template ListIteratorImp a sintaxe para distinguir a sobrecarga do operador de pré do de pós incremento.)Com esses operadores sobrecarregados, o template da função iterate pode agora ser escrito como:
template <>
void Print(ListImp<>& List)
{
for(ListIteratorImp<> Lit(List); Lit(); ++Lit)
Print(Lit.Current());
}


operator int()
{

return Cur!='NULL';

}

T* operator ++(int)
{

return Next();

}

T* operator ++()
{

if(Cur)

Cur=Cur->Next;

return Cur;

}
RTTI significa Informações de Tipo em Tempo de Execução, em C++ um tipo é representado por uma estrutura do tipo "typeinfo" a qual é declarada no arquivo de cabeçalho #include <>, em tempo de execução o tipo de uma expressão, e o nome do tipo como o operador typeid. A sintaxe é
typeid(expressão)
typeid(nome de tipo)

O operador typeid retorna o typeinfo como argumento. O método mais útil de typeinfo é name, no qual retorna um ponteiro para .......Ascz.......... contendo um nome do tipo. Por exemplo:
#include <>
#include <> class Banana{....};
int main()
{
printf(typeid(Banana).name());
}
void PrintName(Primitivo2i)* p)
{
cout<< p="=">}
Observação: para contornar um bug do GCC podemos escrever
#include<>
void PrintName(const type_info& info)
{
const char* name= info.name();
while(isdigit(*name))
name++;
cout <<>

}

void PrintName(Primitvo2D* p)
{
if( p==0)
cout << "null";
else
PrintName(typeid(*p));
}

Outro método útil da classe Type_info é o operador de comparação ==.

Os novos operadores de cast, aquele que é relaciona com a DinamicCast o qual tenta converter em tempo de execução a expressão passada como argumento para o tipo entre < >. Se a conversão puder ser feita DinamicCast retorna um ponteiro não nulo. Caso contrário o argumento não pode ser convertido para o tipo especificado. O trecho de código a seguir ilustra:

Primitivo2D* p;
Line2D* l;,br>...
l=dynamic_cast<> (p);
if(l!=0)
ProcessaLinha2D(l);
DinamicCast é a forma segura e que sempre funciona de fazer:

if(typeid(*p)==typeid(Line2D))
l=(Line2D*)p;

nem sempre permitido pelo compilador.

0 comentários:

 
Design by Free WordPress Themes | Bloggerized by Lasantha - Premium Blogger Themes | Justin Bieber, Gold Price in India