Introdução
Na programação C ++ básica, o tipo de dados, e.g., int ou char, deve ser indicado em uma declaração ou definição. Um valor como 4 ou 22 ou -5 é um int. Um valor como 'A' ou 'b' ou 'c' é um caractere. O mecanismo de modelo permite ao programador usar um tipo genérico para um conjunto de tipos reais. Por exemplo, o programador pode decidir usar o identificador T para int ou char. É possível que um algoritmo C ++ tenha mais de um tipo genérico. Com, digamos, T para int ou char, U pode representar o tipo float ou ponteiro. Uma classe, como string ou classe vetorial, é como um tipo de dados, e os objetos instanciados são como valores do tipo de dados, que é a classe especificada. Portanto, o mecanismo de modelo também permite que o programador use um identificador de tipo genérico para um conjunto de classes.
Um modelo C ++ cria um algoritmo independente do tipo de dados empregados. Assim, o mesmo algoritmo, com muitas ocorrências do mesmo tipo, pode usar diferentes tipos em diferentes execuções. As entidades de variável, função, estrutura e classe podem ter modelos. Este artigo explica como declarar modelos, como definir modelos e como aplicá-los em C++. Você já deve ter conhecimento das entidades mencionadas para entender os tópicos abordados neste artigo.
Tipos
Escalar
Os tipos escalares são void, bool, char, int, float e pointer.
Classes como tipos
Uma classe particular pode ser considerada como um tipo e seus objetos como valores possíveis.
Um tipo genérico representa um conjunto de tipos escalares. A lista de tipos escalares é extensa. O tipo int, por exemplo, tem outros tipos relacionados, como short int, long int, etc. Um tipo genérico também pode representar um conjunto de classes.
Variável
Um exemplo de declaração e definição de modelo é o seguinte:
modeloT pi = 3.14;
Antes de continuar, observe que este tipo de declaração não pode aparecer na função main () ou em qualquer escopo de bloco. A primeira linha é a declaração do template-head, com o nome-tipo genérico escolhido pelo programador, T. A próxima linha é a definição do identificador, pi, que é do tipo genérico, T. A precisão, se o T é um int ou um float ou algum outro tipo, pode ser feita na função main () do C ++ (ou alguma outra função). Tal precisão será feita com a variável pi, e não com T.
A primeira linha é a declaração do template-head. Esta declaração começa com a palavra reservada, modelo e, em seguida, os colchetes angulares abertos e fechados. Dentro dos colchetes angulares, há pelo menos um identificador de tipo genérico, como T, acima. Pode haver mais de um identificador de tipo genérico, com cada um precedido pela palavra reservada, nome de tipo. Esses tipos genéricos nessa posição são chamados de parâmetros de modelo.
A seguinte declaração pode ser escrita em main () ou em qualquer outra função:
cout << piE a função exibiria 3.14. A expressão pi
Na especialização, o tipo de dado escolhido, como float, é colocado entre colchetes angulares após a variável. Se houver mais de um parâmetro de modelo na declaração do cabeçalho do modelo, haverá um número correspondente de tipos de dados na mesma ordem na expressão de especialização.
Na especialização, um tipo é conhecido como argumento de modelo. Não confunda entre isso e o argumento da função para chamada de função.
Tipo Padrão
Se nenhum tipo for fornecido na especialização, o tipo padrão é assumido. Portanto, a partir da seguinte expressão:
modeloU pi = "amor";
a exibição de:
cout << pi<> << '\n';
é "amor" para o ponteiro constante para char. Observe na declaração que U = const char *. Os colchetes angulares estarão vazios na especialização (nenhum tipo fornecido); o tipo real é considerado um ponteiro const para char, o tipo padrão. Se algum outro tipo fosse necessário na especialização, o nome do tipo seria escrito entre colchetes angulares. Quando o tipo padrão é desejado na especialização, repetir o tipo nos colchetes é opcional, i.e., os colchetes angulares podem ser deixados vazios.
Nota: o tipo padrão ainda pode ser alterado na especialização por ter um tipo diferente.
estrutura
O exemplo a seguir mostra como um parâmetro de modelo pode ser usado com uma estrutura:
modeloT John = 11;
T Peter = 12;
T Mary = 13;
T Joy = 14;
;
Essas são as idades dos alunos em uma série (classe). A primeira linha é a declaração do modelo. O corpo entre colchetes é a definição real do modelo. As idades podem ser geradas na função main () com o seguinte:
Idadescout << grade7.John << " << grade7.Mary << '\n';
O resultado é: 11 13. A primeira instrução aqui realiza a especialização. Observe como foi feito. Ele também dá um nome para um objeto da estrutura: grade7. A segunda instrução tem expressões de objetos de estrutura comuns. Uma estrutura é como uma classe. Aqui, Ages é como o nome de uma classe, enquanto grade7 é um objeto da classe (struct).
Se algumas idades forem inteiras e outras flutuantes, a estrutura precisará de dois parâmetros genéricos, como segue:
modeloT John = 11;
U Peter = 12.3;
T Mary = 13;
U Joy = 14.6;
;
Um código relevante para a função main () é o seguinte:
Idadescout << grade7.John << " << grade7.Peter << '\n';
O resultado é: 11 12.3. Na especialização, a ordem dos tipos (argumentos) deve corresponder à ordem dos tipos genéricos na declaração.
A declaração do modelo pode ser separada da definição da seguinte forma:
modeloT John;
U Peter;
T Mary;
U Joy;
;
Idades
O primeiro segmento de código é puramente uma declaração de um modelo (não há atribuições). O segundo segmento de código, que é apenas uma declaração, é a definição do identificador, grau7. O lado esquerdo é a declaração do identificador, grau7. O lado direito é a lista de inicializadores, que atribui valores correspondentes aos membros da estrutura. O segundo segmento (instrução) pode ser escrito na função main (), enquanto o primeiro segmento permanece fora da função main ().
Não Tipo
Exemplos de tipos que não são de dados incluem int, ponteiro para objeto, ponteiro para função e tipos automáticos. Existem outros não tipos, que este artigo não aborda. Um não tipo é como um tipo incompleto, cujo valor é fornecido posteriormente e não pode ser alterado. Como um parâmetro, ele começa com um não-tipo específico, seguido por um identificador. O valor do identificador é fornecido posteriormente, na especialização, e não pode ser alterado novamente (como uma constante, cujo valor é fornecido posteriormente). O programa a seguir ilustra isso:
#incluirusando namespace std;
modelo
T John = N;
U Peter = 12.3;
T Mary = N;
U Joy = 14.6;
;
int main ()
Idades
cout << grade7.John << " << grade7.Joy << '\n';
return 0;
Na especialização, o primeiro tipo, int, entre os colchetes é mais para formalidade, para se certificar de que o número e a ordem dos parâmetros correspondem ao número e a ordem dos tipos (argumentos). O valor de N foi dado na especialização. O resultado é: 11 14.6.
Especialização Parcial
Suponhamos que um template tenha quatro tipos genéricos e que, entre os quatro tipos, haja a necessidade de dois tipos padrão. Isso pode ser alcançado usando a construção de especialização parcial, que não emprega o operador de atribuição. Portanto, a construção de especialização parcial fornece valores padrão para um subconjunto de tipos genéricos. No entanto, no esquema de especialização parcial, uma classe base (estrutura) e uma classe de especialização parcial (estrutura) são necessárias. O programa a seguir ilustra isso para um tipo genérico de dois tipos genéricos:
#incluirusando namespace std;
// classe de modelo base
modelo
Idade Estrutural
;
// especialização parcial
modelo
Idade Estrutural
T1 João = 11;
flutuar Peter = 12.3;
T1 Maria = 13;
float Joy = 14.6;
;
int main ()
Idades
cout << grade7.John << " << grade7.Joy << '\n';
return 0;
Identifique a declaração da classe base e sua definição parcial de classe. A declaração do template-head da classe base tem todos os parâmetros genéricos necessários. A declaração template-head da classe de especialização parcial tem apenas o tipo genérico. Existe um conjunto extra de colchetes angulares usados no esquema que vem logo após o nome da classe na definição de especialização parcial. É o que realmente faz a especialização parcial. Possui o tipo padrão e o tipo não padrão, na ordem escrita na classe base. Observe que o tipo padrão ainda pode receber um tipo diferente na função main ().
O código relevante na função main () pode ser o seguinte:
Idadescout << grade7.John << " << grade7.Joy << '\n';
O resultado é: 11 14.6.
Pacote de parâmetros de modelo
Um pacote de parâmetros é um parâmetro de modelo que aceita zero ou mais tipos genéricos de modelo para os tipos de dados correspondentes. O parâmetro do pacote de parâmetros começa com a palavra reservada nome de tipo ou classe. Isso é seguido por três pontos e, em seguida, o identificador do pacote. O programa a seguir ilustra como um pacote de parâmetros de modelo pode ser usado com uma estrutura:
#incluirusando namespace std;
modelo
int John = 11;
flutuar Peter = 12.3;
int Mary = 13;
float Joy = 14.6;
;
int main ()
Idades
cout << gradeB.John << " << gradeB.Mary << '\n';
Idades
cout << gradeC.Peter << " << gradeC.Joy << '\n';
Idades
cout << gradeD.John << " << gradeD.Joy << '\n';
Idades <> grauA; // como padrão
cout << gradeA.John << " << gradeA.Joy << '\n';
return 0;
O resultado é:
11 1312.3 14.6
11 14.6
11 14.6
Modelos de Função
Os recursos do modelo mencionados acima se aplicam de maneira semelhante aos modelos de função. O programa a seguir mostra uma função com dois parâmetros de modelo genéricos e três argumentos:
#incluirusando namespace std;
modelo
cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';
int main ()
função (12, '$', "500");
return 0;
O resultado é o seguinte:
Existem 12 livros no valor de $ 500 na loja.
Separação do Protótipo
A definição da função pode ser separada de seu protótipo, como mostra o seguinte programa:
#incluirusando namespace std;
modelo
modelo
cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';
int main ()
função (12, '$', "500");
return 0;
Nota: A declaração do modelo de função não pode aparecer na função main () ou em qualquer outra função.
Sobrecarregando
A sobrecarga da mesma função pode ocorrer com diferentes declarações de template-head. O programa a seguir ilustra isso:
#incluirusando namespace std;
modelo
cout << "There are " << no << " books worth " << cha << str << " in the store." << '\n';
modelo
cout << "There are " << no << " books worth $" << str << " in the store." << '\n';
int main ()
função (12, '$', "500");
função (12, "500");
return 0;
O resultado é:
Existem 12 livros no valor de $ 500 na loja.
Existem 12 livros no valor de $ 500 na loja.
Modelos de classes
Os recursos dos modelos mencionados acima se aplicam de maneira semelhante aos modelos de classe. O programa a seguir é a declaração, definição e uso de uma classe simples:
#incluirusando namespace std;
classe TheCla
público:
int num;
estático char ch;
void func (char cha, const char * str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
static void fun (char ch)
if (ch == 'a')
cout << "Official static member function" << '\n';
;
int main ()
TheCla obj;
obj.num = 12;
obj.func ('$', "500");
return 0;
O resultado é o seguinte:
Existem 12 livros no valor de $ 500 na loja.
O programa a seguir é o programa acima com uma declaração de template-head:
#incluirusando namespace std;
modelo
público:
T num;
estático U ch;
void func (U cha, const char * str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
static void fun (U ch)
if (ch == 'a')
cout << "Official static member function" << '\n';
;
int main ()
TheCla
obj.num = 12;
obj.func ('$', "500");
return 0;
Em vez da palavra typename na lista de parâmetros do modelo, a palavra class pode ser usada. Observe a especialização na declaração do objeto. O resultado ainda é o mesmo:
Existem 12 livros no valor de $ 500 na loja.
Declaração de Separação
A declaração do modelo de classe pode ser separada do código da classe, da seguinte maneira:
modelomodelo
público:
T num;
estático U ch;
void func (U cha, const char * str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
static void fun (U ch)
if (ch == 'a')
cout << "Official static member function" << '\n';
;
Lidando com membros estáticos
O programa a seguir mostra como acessar um membro de dados estáticos e uma função de membro estático:
#incluirusando namespace std;
modelo
público:
T num;
estático U ch;
void func (U cha, const char * str)
cout << "There are " << num << " books worth " << cha << str << " in the store." << '\n';
diversão void estática (U cha)
if (ch == 'a')
cout << "Official static member function" << cha << '\n';
;
modelo
int main ()
TheCla
return 0;
Atribuir um valor a um membro de dados estáticos é uma declaração e não pode estar em main (). Observe o uso e as posições dos tipos genéricos e do tipo genérico de dados na instrução de atribuição. Além disso, observe que a função-membro de dados estáticos foi chamada em main (), com os tipos de dados de modelo reais. O resultado é o seguinte:
Função de membro estático oficial.
Compilando
A declaração (cabeçalho) e a definição de um modelo devem estar em um arquivo. Ou seja, eles devem estar na mesma unidade de tradução.
Conclusão
Os modelos C ++ fazem um algoritmo independente do tipo de dados empregados. As entidades de variável, função, estrutura e classe podem ter modelos, que envolvem declaração e definição. A criação de um modelo também envolve especialização, que ocorre quando um tipo genérico assume um tipo real. A declaração e a definição de um modelo devem estar em uma unidade de tradução.