
Gabaritos
Sobrecarregando Gabaritos
Gabaritos de Classes
Gabaritos de classe e parâmetros não-tipo
Gabaritos e Herança
Gabaritos e friends
Gabaritos e membros static
•
Gabaritos
Funções sobrecarregadas são normalmente usadas para executar
operações semelhantes sobre tipos de dados diferentes. Se as operações
são idênticas para cada tipo de, isto pode ser executado mais compacta
e convenientemente usando-se gabaritos de função.
Todas as definições de gabaritos começam com a palavra-chave
template seguida por uma lista de parâmetros de tipos formais para o gabarito
de função.
Exemplo
#include <iostream>
using std::cout;
using std::endl;
template< class Tipo >
void imprimir_array( const Tipo *varray, const int total )
{
for ( int i=0; i<total; i++ )
{
cout << varray[i] << " ";
}
cout << endl;
}
int main()
{
const int total1 = 5, total2 = 7, total3 = 4;
int a[total1] = { 1, 2, 3, 4, 5 };
double b[total2] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 };
char c[total3] = "ALO";
cout << "Arrays " << endl;
imprimir_array( a, total1 );
cout << "Arrays " << endl;
imprimir_array( b, total2 );
cout << "Arrays " << endl;
imprimir_array( c, total3 );
system("pause");
return 0;
}
Sobrecarregando Gabaritos
Podemos sobrecarregar funções gabaritos de modo que o compilador
usa a resolução de sobrecarga para invocar a função
apropriada.
Por exemplo, a função imprimir array poderia ter parâmetros
adicionais como maior índice e menor índice para varrer apenas
entre alguns índices.
Gabaritos de Classes
É possível compreender o que é uma pilha, independente
do tipo de itens que estão sendo colocados nela. Mas quando chega a hora
de instancia-las um tipo deve ser especificado. Isto cria uma oportunidade maravilhosa
para a reutilização de software. Necessitados dos meios de descrever
a noção de uma pilha genericamente e instanciar classes que são
versões desta classe genérica para tipos específicos. Este
recursos é fornecido por gabaritos em C++.
Gabaritos de classes são chamados de tipos
parametrizados, por que
exigem um ou mais parâmetros de tipo para especificar como personalizar
um gabarito de uma classe genérica para formar um gabarito de classe especifico.
Vamos demonstrar o exemplo da pilha.
Existem duas restrições para tipos de dados não primitivos
no exemplo: eles devem ter um construtor default e devem suportar o operador
de atribuição.
As definições de funções das classes gabaritos devem ficar no arquivo .h
// tstatck.h
#ifndef TSTACK_H
#define TSTACK_H
// uma stack é uma pilha
template< class Tipo >
class Pilha {
public :
Pilha( int = 10 );
~Pilha();
//~Pilha() { delete [] ponteiro; }
// push = inserir
bool push( const Tipo & );
// pop = excluir
bool pop( Tipo & );
private:
int tamanho;
int topo;
// ponteiro para pilha
Tipo *ponteiro;
//bool vazio() const { return topo == -1; };
bool vazio() const;
//bool cheio() const { return topo
== tamanho - 1; }
bool cheio() const;
};
// definiçoes em .h
template< class Tipo >
Pilha< Tipo >::Pilha( int vtamanho)
{
tamanho = vtamanho > 0 ? vtamanho : 10;
// inicialmente a pilha está fazia
topo = -1;
// aloca espaço para
elementos
ponteiro = new Tipo[ tamanho ];
}
// destrutor
template< class Tipo >
Pilha< Tipo >::~Pilha()
{
delete [] ponteiro;
}
// Insere um elemento na pilha
template< class Tipo >
bool Pilha< Tipo >::push( const Tipo &novo_item )
{
if ( !cheio() )
{
ponteiro[ ++topo ] = novo_item;
return true;
}
return false;
}
template< class Tipo >
bool Pilha< Tipo >::pop( Tipo &item )
{
if ( !vazio() )
{
item = ponteiro[ topo-- ];
return true;
}
return false;
}
template< class Tipo >
bool Pilha< Tipo >::vazio() const
{
return topo == -1;
}
template< class Tipo >
bool Pilha< Tipo >::cheio() const
{
return topo == tamanho - 1;
}
#endif
// main.cpp
#include <iostream>
using std::cout;
using std::cin;
using std::endl;
#include "tstack.h"
int main()
{
Pilha< double > Tdouble_pilha( 5 );
double f = 1.1;
cout << "Inserindo elementos " << endl;
while ( Tdouble_pilha.push( f ) )
{
cout << f << " ";
f += 1.1;
}
cout << "\n\nA pilha ficou cheia " << endl;
cout << "Retirando elementos" << endl;
while ( Tdouble_pilha.pop( f ) )
{
cout << f << " ";
}
cout << "\n\nA pilha ficou vazia " << endl;
Pilha< int > Tint_pilha( 5 );
int a = 1;
cout << "Inserindo elementos " << endl;
while ( Tint_pilha.push( a ) )
{
cout << a << " ";
a += 1;
}
cout << "\n\nA pilha ficou cheia " << endl;
cout << "Retirando elementos" << endl;
while ( Tint_pilha.pop( a ) )
{
cout << a << " ";
}
cout << "\n\nA pilha ficou vazia " << endl;
system("pause");
return 0;
}
Gabaritos de classe e parâmetros não-tipo
O gabarito de classe stack( pilha ) da seção anterior usou somente
parâmetros de tipo no cabeçalho do gabarito. Também é possível
usar parâmetros não-tipo; um parâmetro não tipo pode
ter um argumento default e o parâmetro é tratado como const.
Exemplo:
template< class Tipo, int elements >
declarado como
Pilha< double, 100 > vnossa_pilha;
Durante a compilação, pilha será declarada como um double de 100 elementos.
Isso elimina, quando possivel, o overhead de criar espaço com new.
Gabaritos e Herança
-> Um gabarito de classe pode ser derivado de uma classe gabarito.
-> Um gabarito de classe pode ser derivado de uma classe não-gabarito.
-> Uma classe gabarito pode ser derivada de um gabarito de classe.
-> Uma classe não-gabarito pode ser derivada de um gabarito de classe.
Gabaritos e friends
-> Dentro de um gabarito de classe para a classe X que foi declarado com
template< class Tipo > class X
uma declaração de friend da forma
friend void f1().
torna a função f1 um friend de toda classe gabarito instanciadas a partir
do gabarito de classe precedente.
-> Dentro de um gabarito de classe para a classe X que foi declarado com
templace< class Tipo > class X
uma declaração de friend da forma
friend void f2( X< Tipo >& );
para um tipo particular Tipo, tal como float, torna uma função f2 um friend
somente de X<float>.
-> Dentro de um gabarito de classe, você pode declarar que uma função membro
de outra classe é um friend de qualquer classe gabarito gerada a partir do gabarito
de classe. Simplesmente nomeie a função membro de uma outra classe usando o nome
de classe e o operador de resolução de escopo binário.
template< class Tipo > class X
uma declaração de friend da forma
friend void A::f4();
torna a função membro f4 da classe A um friend de toda classe gabarito instanciada
a partir do gabarito de classe precedente.
-> Dentro de um gabarito de classe para a classe X que foi declarado com
template< class Tipo > classe X
uma declaração de friend da forma
friend void C < Tipo >::f5( X< Tipo > & );
para um tipo particular Tipo, tal como float, torna a função membro
C< float >::f5( X< float > & )
uma função friend somente da classe gabarito X<
float >.
-> Dentro de um gabarito de classe para a classe X que foi declarado com
template< class Tipo > class X
uma segunda classe Y pode ser declarada com
friend class Y;
tornando toda função membro da classe Y um friend de toda classe gabarito
produzida a partir do gabarito de classe para X.
-> Dentro de um gabarito de classe para a classe X que foi declarado com
template< class Tipo > class X
uma segunda classe Z pode ser declarada com
friend class Z< T >;
então, quando uma classe gabarito é instanciada com um tipo particular para
Tipo, tal como float, todos os membros de class Z<
float > tornam-se friends
da classe gabarito X< float >
Gabaritos e membros static
Cada classe instanciada a partir de um gabarito de classe tem sua propria
copia de cada membro de dados static do gabarito de classe; todos os objetos
daquela classe compartilham aquele membro de dados static. O membro deve ser
iniciado no escopo do arquivo.
|