O uso da declaração template em C++ permite definir classes e funções generalizadas. No caso de uma classe template, podemos definir classes em que existam propriedades que podem assumir um tipo qualquer de dados ou objectos. Estas classes têm um comportamento especial e o código delas só é gerado quando o compilador encontra a sua utilização. Note que, se usarmos vários tipos, o compilador irá gerar código para tantas classes quantos tipos forem usados, gerando cada uma de acordo com cada tipo.
Vamos considerar o seguinte exemplo:
Pretende-se criar uma classe que admita um par de valores de qualquer tipo (int, float, string, …) e que possa devolver qual o valor maior (max) e o qual valor menor (min).
Vejamos como podemos definir esta classe:
template <class T> class Par { T val1; T val2; public: Par(T v1, T v2) : val1(v1), val2(v2) {} T max() { return val1 > val2 ? val1 : val2; } T min() { return val1 < val2 ? val1 : val2; } };
Na linha 1, usamos a declaração “template <class T>” para indicar que estamos perante um template de uma classe que irá poder receber um tipo genérico “T“. Nas linhas 3 e 4, definimos as propriedades “val1” e “val2” como sendo deste tipo genérico “T”. No próprio construtor, na linha 6, indicamos que os parâmetros são também do tipo genérico “T”, assim como as funções max() e min() nas linhas seguintes.
Se tivermos duas variáveis do tipo inteiro, i1 e i2, podemos usar esta classe da seguinte forma:
Par<int> pInt(i1, i2); cout << "O inteiro maior é: " << pInt.max() << endl; cout << "O inteiro menor é: " << pInt.min() << endl;
Repare na forma como declaramos “pInt” como sendo uma instância de “Par<int>”. Neste momento, o compilador irá gerar o código para a classe “Par” em que o tipo genérico “T” será do tipo “int“. É como se declarássemos a seguinte classe:
class Par { int val1; int val2; public: Par(int v1, int v2) : val1(v1), val2(v2) {} int max() { return val1 > val2 ? val1 : val2; } int min() { return val1 < val2 ? val1 : val2; } };
O mesmo aconteceria se tivéssemos usado qualquer outro tipo! Dando mais um exemplo, imagine que declaramos uma variável como uma instância de “Par<string>”. A classe gerada, seria do tipo:
class Par { string val1; string val2; public: Par(string v1, string v2) : val1(v1), val2(v2) {} string max() { return val1 > val2 ? val1 : val2; } string min() { return val1 < val2 ? val1 : val2; } };
Note que, tudo isto é gerado durante o tempo de compilação e estes dois últimos exemplos são isso mesmo – exemplos – e que ilustram as classes equivalentes, caso não usássemos templates! Nós não vimos este código a ser gerado.
O seguinte programa completo utiliza três tipos diferentes para a classe template “Par<T>”:
#include <iostream> #include <stdlib.h> #include <string> using namespace std; template <class T> class Par { T val1; T val2; public: Par(T v1, T v2) : val1(v1), val2(v2) {} T max() { return val1 > val2 ? val1 : val2; } T min() { return val1 < val2 ? val1 : val2; } }; int main() { int i1, i2; float f1, f2; string s1, s2; cout << "Introduza 2 valores inteiros:" << endl; cin >> i1; cin >> i2; Par<int> pInt(i1, i2); cout << "O inteiro maior é: " << pInt.max() << endl; cout << "O inteiro menor é: " << pInt.min() << endl; cout << "Introduza 2 valores do tipo float:" << endl; cin >> f1; cin >> f2; Par<float> pFloat(f1, f2); cout << "O float maior é: " << pFloat.max() << endl; cout << "O float menor é: " << pFloat.min() << endl; cout << "Introduza 2 strings:" << endl; cin >> s1; cin >> s2; Par<string> pStr(s1, s2); cout << "A string maior é: " << pStr.max() << endl; cout << "A string menor é: " << pStr.min() << endl; }
Exemplo da execução do programa:
30
40
O inteiro maior é: 40
O inteiro menor é: 30
Introduza 2 valores do tipo float:
12.4
5.6
O float maior é: 12.4
O float menor é: 5.6
Introduza 2 strings:
abc
def
A string maior é: def
A string menor é: abc
Nota importante: Quando declaramos as nossas classes em ficheiros separados, normalmente escrevemos a definição da classe num ficheiro de cabeçalho “.h” e a implementação num ficheiro “.cpp”. Quando usamos classes template não podemos fazer esta separação pois o compilador precisa de gerar tudo junto. Caso contrário, durante a fase de “linkagem“, irá ser exibida uma mensagem de erro pois o “linker” não vai encontrar a implementação gerada pelo compilador! Neste caso, coloque tudo num ficheiro “.h”.
Para ver como usar funções template, veja o artigo: “Funções template em C++“
Siga-nos nas redes sociais e esteja a par das nossas novidades no nosso blogue multi-temático Out4Mind!
Este artigo foi escrito de acordo com a antiga grafia.