C++ implementa templates os quais podem ser usados em classes e
funções. Para exemplificar seu uso em classes, sejam as classes
abaixo, as quais representam um elemento de uma lista linearmente
encadeada e a lista propriamente dita:
Ex.
- struct Banana
- float L;
float D;
Banana *Next;
{
}
- class ListBanana
- public:
- ListBanana();
~ListBanana();
void Add(Banana*);
void Remove(Banana*);
int GetNumberOfElements() const;
bool IsEmpty() const;
void Clear();
Banana *PeekHead() const;
private:
Banana *Head;
Banana *Tail;
int NumberOfElements;
ListBanana(const ListBanana&); // Não pode acessar- //os métodos private, logo não há construtor de cópia
ListBanana& operator=(const ListBanana&);
- ListBanana();
{
};
while(Head)
{
Banana *temp = Head;
Head = Head -> next;
delete temp;
}
Tail = NumberOfElements = 0;
void ListBanana :: Clear()
{
}
OBS:
- Banana b; // Cria objeto
List
// classe (instância do template); objeto (instância da classe);
typedef List
ListBanana lb;
Vamos supor agora que se queira implementar uma lista de um
outro tipo qualquer, por exemplo: "Laranja". Sem templates há
duas alternativas:
1. Replicar a implementação da lista trocando "Banana" por "Laranja". Isto é incoveniente por razões óbvias, dentre as quais, há necessidade de alterações manuais no código replicado, sempre que um novo método seja adicionado a classe ou que algum método já existente seja alterado. 2. Para evitar a replicação, os elementos de uma lista seriam de classes todas elas derivadas de uma classe chamada:
- class ListElement
- private:
- ListElement *Next;
friend class List;
- ListElement *Next;
{
};
Assim, "Banana" e "Laranja" derivariam de "ListElement" e haveria
apenas uma implementeção da classe "List", cujos elementos seria
do tipo "ListElement". O incoveniente é que um objeto do tipo
"List" seria um contêiner de quaisquer objeto do tipo "ListElement",
ou seja, poderíamos ter na mesma lista "Bananas" e "Laranjas".
As vezes é justamente isso que se quer, mas nem sempre.
Quando for este o caso, podemos usar templates.
Uma lista genérica pode ser definida em C++ através do seguinte template:
- template
- public:
ListImp();
~ListImp();
void Add(T*);
void Remove(T*);
int GetNumberOfElements() const;
bool IsEmpty() const;
void Clear();
T *PeekHead() const;
void AddAtHead(T*);- T *Head;
T *Tail;
int NumberOfElements;
ListImp(const ListImp&);
ListImp& operator=(const ListImp &);
private:
class ListImp
{
}
- template
- while(Head)
- T *temp = Head;
Head = Head -> next;
delete temp;
{
}
Tail = NumberOfElements = 0;- T *temp = Head;
void ListImp
{
}
No exemplo acima, "
um tipo qualquer chamado "T". Note que no corpo da classe, "T"
é usado para representar o tipo de atributos, valores de retorno de
método e parâmetros de método. Um template pode ter qualquer
número positivo de parâmetros.
Um template de uma classe não é uma classe, mas um molde a
partir do qual o compilador gera uma classe. Gerar uma classe a
partir de um template, significa instanciar o template. Para tal,
basta escrever o nome da classe e fornecer um argumento:
... ListImp
- ListImp <> | B;
Na declaração acima ocorre a criação da classe que representa uma lista de Bananas, se esta ainda não tiver sido criada pelo compilador(instanciação do template) e em seguida a criação de um objeto chamado LB do tipo List da Banana(instanciação da classe).
Instanciação de template é igual a classe e instanciação de classe é igual a objeto
Cada instanciação de um template com diferentes argumentos replica todo o código definido no molde. Isto é oque faríamos manualmente no item 1 listado anteriormente; A vantagem é que a tarefa é executada automaticamente pelo compilador. Mas tenha em mente que a replicação ocorre. Portanto embora seja uma solução interessante em muitos casos templates devem ser usados criteriosamente. No caso de lista os métodos são simples e sua replicação não aumentaria demais o tamanho do programa objeto. Isto não ocorre com tudo, em estruturas que requerem um código mais complexo, como por exemplo AVLs. Nesse caso poderíamos criar uma classe base e implementaria os métodos mais complicados e que operariam sobre um tipo genérico como feito a seguir:
- class AVLNode
- public:
- AVLNode* Righ;
AVLNode* Left;
int Balance;
- AVLNode* Righ;
{
};
- class AVLBase
- AVLNode* Root;
int NumberOfElements;
{
private:
- protected:
- void Add(AVLNode* );
...
};
A classe AVLBase implementa as operações de inserção, remoção, e atravessamento de uma AVL considerando que os elementos são do tipo AVLNode. Esses métodos são declarados protegidos em AVLBase.
- template <>
- T Value;
...
class AVLElement: public AVLNode
{
public:
};
- template <>
- void Add(const TB Value)
{- AVLBase::Add(new AVLElement <> (Value));
}
...
class AVL: public AVLBase
{
public:
};
O template AVLElement gera classes cujos objetos são nós tipo AVL com valor do tipo T. O template AVL gera classes cujos objetos são AVLs de elementos do tipo T. Note que agora a instanciação desse template não replica o código complicado da estrutura de dados AVL o qual foi implementado em AVLBase.
No template ListImp o tipo T deve ser uma classe que declara como atributo público Next. Esta exigência impede que usemos o ListImp para gerar uma lista de Inteiros por exemplo. Isto pode ser contornado com os seguintes templates:
- Template
- public:
- List();
- ~List();
void Add(T*); - void AddAtHead(T*);
void Remove(T*);
int GetNumerOfElements() const;
bool IsEmpty() const;
void Clear();
T* PeekHead() const; - private:
- T* Tail;
- T* Head;
int NumberOfElements;
List(const List<>&);
List operator=(const List<>&); - }
class List
{
O template List gera classes cujos objetos são listas de elementos cujo valor são do tipo T. Supoe-se agora que sobre o tipo T sejam definidas as operações de cópia e comparação(verifique).


7:14 AM
Unknown
0 comentários:
Postar um comentário