Buscar

15 - Capítulo 12 - Gabaritos

Prévia do material em texto

Gabaritos 
Objetivos 
• Ser capaz de usar gabaritos de funções para criar um grupo 
de funções relacionadas (sobrecarregadas). 
• Ser capaz de distinguir entre gabaritos de funções e fun çõe gabarito. 
• Ser capaz de usar gabaritos de classes para criar um grupo 
de tipos relacionados. 
• Ser capaz de distinguir entre gabaritos de classe e classes 
gabarito. 
• Entender como sobrecarregar funções gabarito. 
• Entender os relacionamentos entre gabaritos, friends, he- L 
rança e membros estáticos. 
Por trás daquele padrão externo as formas sombrias ficam mais 
claras a cada dia. 
E sempre a ínesma forma, apenas muito numerosa 
Charlotte Perkins Gilman 
The Yellow Wallpaper 
Se você conseguir deslizar entre os parâmetros dos céus e da terra, deslize. 
O Corão 
Um labirinto poderoso! mas não sem um plano. 
Alexander Pope 
12 
680 C+÷ COMO PROGRAMAR 
Visão geral 
12.1 Introdução 
12.2 Gabaritos de função 
12.3 Sobrecarregando funções gabarito 
12.4 Gabaritos de classe 
12.5 Gabaritos de classe e parâmetros não-tipo 
12.6 Gabaritos e herança 
12.7 Gabaritos e friends 
12.8 Gabaritos e membros static 
Resumo . Terminologia . Erros comuns de programação Dicas de desempenho. Observações de engenharia de software . Dica de teste e depura ção • Exercícios de auto-revisão Respostas aos exercícios de auto- revisão • Exercícios 
12.1 Introdução 
Neste capítulo, discutimos um dos recursos mais poderosos de C++, ou seja, os gabaritos. Os gabaritos nos possibilitam especificar, com um único segmento de código, uma gama inteira de funções relacionadas (sobrecarregadas) - chamadas de funções gabarito - ou uma gama inteira de classes relacionadas - chamadas de classes gabarito. 
Podemos escrever um único gabarito de função para uma função de classificação de arrays e então fazer com que C++ gere automaticamente funções gabarito separadas que classificam um array int, classificam um array float, classificam um array de strings, e assim por diante. 
Discutimos gabaritos de função no Capítulo 3. Para benefício daqueles leitores que saltaram aquele tópico, apresentamos uma discussão e um exemplo adicionais neste capítulo. 
Podemos escrever um gabarito de classe único para uma classe pilha e então fazer com que C++ gere classes gabaritos separadas, tais como uma classe pilha de int, uma classe pilha de float, uma classe pilha de string. e assim por diante. 
Note a distinção entre gabaritos de função e funções gabarito: os gabaritos de função e os gabaritos de classe são como estênceis com os quais nós traçamos formas; as funções gabarito e as classes gabarito são como os traçados separados que tem todos a mesma forma, mas poderiam ter sido desenhados, por exemplo, com cores diferentes. 
Observação de engenharia de software 12.1 
______ Os gabaritos são um dos recursos mais poderosos de C+ + para a reutilização de software. 
Neste capítulo, apresentamos exemplos de um gabarito de função e um gabarito de classe. Também consideraremos as relações entre gabaritos e outras características de C++, tais como sobrecarga, herança, friends e membros static. 
O projeto e os detalhes dos mecanismos de gabarito discutidos aqui se baseiam no trabalho de Bjarne Stroustrup apresentado no seu artigo Parameterized Tvpes for C+ + e publicado nos Proceedings ofthe USENIX C+ + Conference, ocorrido em Denver, Cobrado, em outubro de 1988. 
Este capítulo pretende ser somente uma breve introdução ao rico e complexo tópico de gabaritos. O Capítulo 20, “A biblioteca padrão de gabaritos (STL)”, o maior capítulo deste livro, apresenta um tratamento detalhado das classes gabarito contêineres, iteradores e algoritmos da STL. O Capítulo 20 contém dezenas de exemplos de código real baseados em gabaritos - ilustrando técnicas de programação com gabaritos mais sofisticadas que as usadas aqui no Capítulo 12. 
CAPÍTULO 12- GABAR1TOS 681 
12.2 Gabaritos de função 
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, isto pode ser executado mais compacta e convenientemente usando-se gabaritos de função. O programador escreve uma única definição do gabarito da função. Baseado nos tipos de argumentos fornecidos explicitamente ou inferidos das chamadas para esta função, o compilador gera funções separadas, no código objeto, para tratar cada tipo de chamada apropriadamente. Em C, esta tarefa pode ser executada usando-se macros criadas com a diretiva #define do pré-processador (ver Capítulo 17, “O pré- processador”). Porém, macros apresentam a possibilidade de sérios efeitos colaterais e não possibilitam ao compilador executar uma verificação de tipo. Os gabaritos de função fornecem uma solução compacta como as macros, mas possibitam uma verificação de tipo completa. 
® Dica de teste e depura ção 12.1 
Gaba ritos dei unção, como macros, possibilitam a reutilização de software. Mas, diferentemente de macros, gabaritos de função ajudam a eliminar muitos tipos de erros por causa do escrutínio da verificação de tipo completa de C++. 
Todas as definições de gabaritos de função começam com a palavra-chave template seguida por uma lista de parâmetros de tipo formais para o gabarito de função, incluso entre os sinais de maior e menor (< e >); cada parâmetro formal de tipo deve ser precedido pela palavra-chave class ou typename, como em 
template< class T > 
ou 
template< typenaine ElementType > 
ou 
template< class BorderType, class FillType > 
Os parâmetros de tipo formais de uma definição de gabarito são usados (como seriam com parâmetros de tipos primitivos ou tipos definidos pelo usuário) para especificar os tipos dos parâmetros da função, especificar o tipo de retorno da função e para declarar variáveis dentro da função. A definição da função vem a seguir e é definida como qualquer outra função. Note que a palavra-chave class ou typename usada para especificar parâmetros de tipo do gabarito de função na realidade significa “qualquer tipo primitivo ou tipo definido pelo usuário”. 
Erro comum de programação 12.1 
Não colocar class (ou typename) antes de cada parâmetro formal de tipo de um gabarito de função. 
Examinemos o gabarito de função printArray na Fig. 12.1. O gabarito de função é usado no programa completo daFig. 12.2. 
1 template< class T > 
2 void printArray( const T *array, const int count 
3 
4 for ( int i = O; i < count; i++ 
5 cout « array[ i 1 « 
6 
7 cout « endl; 
8 
Fig. 12.1 Um gabarito de função. 
682 C++ COMO PROGRAMAR 
O gabarito de função printArray declara um parâmetro formal único T (T poderia ser qualquer identificador válido) para o tipo do array a ser impresso pela função printArray: T * é chamado de parâmetro de tipo. Quando o compilador encontrar uma chamada para a função printArray no código-fonte do programa, o tipo do primeiro parâmetro de printArray é substituído por T na definição do gabarito e C++ cria uma função gabarito completa para imprimir um array do tipo de dados especificado. Então, a função recém-criada é compilada. Na Fig. 12.2, três funções printArray são instanciadas - uma espera um array int. uma espera um array double e uma espera um array char, Por exemplo, a instanciação para o tipo int é: 
void printArray( const int *array, const int count 
for ( int i = O; i < count; i++ ) 
cout « array[ i ] « 
cout « endl; 
Cada parâmetro de tipo formal, em uma definição de gabarito de função, deve normalmente aparecer na lista de parâmetros da função pelo menos uma vez. O nome de um parâmetro de tipo formal pode ser usado somente uma vez na lista de parâmetros de um cabeçalho de gabarito. Nomes de parâmetros de tipo formais entre funções gabarito não precisam ser únicos. 
A Fig. 12.2 ilustra o uso do gabarito de função printArray. O programa começa por instanciar int array a, array double b e char array c, de tamanhos 5, 7, e 4, respectivamente. A seguir, cada um dos arrays é impresso chamando printArray - uma vez com um primeiro parâmetro a do tipo int* , uma vez com um primeiro parâmetro b do tipo double* e uma vez com umprimeiro parâmetro c do tipo char*. A chamada 
printArray( a, aCount ); 
por exemplo, faz com que o compilador infira que T é int e instancie uma função gabarito printArray cujo parâmetro de tipo T é int. A chamada 
printArray( b, bCount ); 
faz com que o compilador infira que T é double e instancie uma segunda função gabarito printArray cujo parâmetro de tipo T é double. A chamada 
printArray( c, cCount ); 
faz com que o compilador infira que T é char e instancie uma terceira função gabarito printArray cujo parâmetro de tipo T é char. 
1 II Fig 12.2: figl2O2.cpp 
2 // Usando funções gabarito 
3 #include <iostream> 
4 
5 using std: :cout; 
6 using std: :endl; 
7 
8 template< class T > 
9 void printArray( const T *array, const int count ) 
10 { 
11 for ( int i = o; i < count; i++ 
12 cout « array[ i ] « 
13 
14 cout « endi; 
15 } 
Fig. 12.2 Usando funções gabarito (parte 1 de 2). 
CAPÍTULO 12 - GABARITOS 683 
16 
17 int main() 
18 { 
19 const int aCount = 5, bCount = 7, cCount = 4; 
20 inta[aCount)={1,2,3,4,5}; 
21 double b[ bCount J = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; 
22 char c[ cCount ] = “ALÔ”; //quarta posição reservada para o caractere nulo 
23 
24 cout « “Array a contém:” « endi; 
25 printArray( a, aCount ); II função gabarito para inteiros 
26 
27 cout « “Array b contém:” « endi; 
28 printArray( b, bCount ); // função gabarito para doulies 
29 
30 cout « “Array c contém:” « endl; 
31 printArray( c, cCount ); // função gabarito para caracteres 
32 
33 return 0; 
34 } 
Array a contém: 
12345 
Array b contém: 
.1 2.2 3.3 4.4 5.5 6.6 7.7 
Array c contém: 
ALÔ 
Fig. 12.2 Usando funções gabarito (parte 2 de 2). 
Neste exemplo, o mecanismo de gabarito poupa o programador de ter que escrever tres runçoes sonrecarregauas separadas, com protótipos 
void printArray(const int “, const int); 
void printArray(const double ‘, const int); 
void printArray(const char , const int); 
as quais usam todas o mesmo código, exceto pelo tipo T. 
Dica de desempenho 12.1 
f Gabaritos certamente oferecem os benefícios de reutilização de software. Mas tenha em mente que múltiplas cópias de funções gabarito e classes gabarito ainda são instanciadas em um programa, apesar do fato de que o gabarito é escrito somente uma vez. Estas cópias podem consumir memória considerável. 
12.3 Sobrecarregando funções gabarito 
Funções gabarito e sobrecarga estão intimamente relacionadas. As funções relacionadas geradas a partir de um gabarito de função têm todas o mesmo nome, de modo que o compilador usa a resolução de sobrecarga para invocar a função apropriada. 
Um gabarito de função pode ser sobrecarregado de vários modos. Podemos fornecer outros gabaritos de função que especificam o mesmo nome da função, mas parâmetros da função diferentes. Por exemplo, o gabarito da função printArray da Fig. 12.2 poderia ser sobrecarregado com outro gabarito da função printArray com parâmetros adicionais lowsubscript e highSubscript para especificar a parte do array a ser impressa (ver Exercício 
12.4). 
684 C++ COMO PROGRAMAR 
Um gabarito de função pode também ser sobrecarregado fornecendo outras funções não-gabarito com o mesmo nome da função, mas com argumentos de função diferentes. Por exemplo, o gabarito de função printArray da Fig. 12.2 poderia ser sobrecarregado com uma versão não-gabarito que imprime especificamente um array de string de caracteres em um formato organizado, tabular, de coluna (ver Exercício 12.5). 
Erro comum de programação 12.2 
Se um gabarito é invocado com um tipo de classe definido pelo usuário e se esse gabarito usa operadores (como +, <, etc.) com objetos do tipo daquela classe, então aqueles operadores devem ser sobrecarregados! Esquecer de sobrecarregar tais operadores provoca erros, porque o com pilador naturalmente, ainda gera chamadas às funções operador sobrecarregado apropriadas, apesar do fato de que estas funções não estão presentes. 
O compilador executa um processo de correspondência para determinar qual função chamar quando uma função é invocada. Primeiro, o compilador tenta achar e usar uma correspondência precisa, na qual os nomes da função e os tipos de parâmetro coincidem exatamente com aqueles da chamada da função. Se isto falha, o compilador verifica se está disponível um gabarito de função que pode ser usado para gerar uma função gabarito com uma correspondência precisa de nome da função e tipos de parâmetros. Se tal gabarito de função é encontrado, o compilador gera e usa a função gabarito apropriada. 
Erro comum deprogramnação 12.3 
O compilador executa um processo de correspondência para determinar qualfunção chamar quando uma função é invocada. Se nenhuma correspondência pode ser encontrada ou se o processo de correspondência produz correspondências múltiplas, é gerado um erro de compilação. 
12.4 Gabaritos de classe 
É possível se compreender o que é uma pilha (uma estrntura de dados na qual inserimos itens em uma ordem e recuperamos os mesmos na ordem inversa, ou seja, “último a entrar, primeiro a sair”), independentemente do tipo dos itens que estão sendo colocados na pilha. Mas quando chega o momento de efetivamente instanciar uma pilha, um tipo de dados deve ser especificado. Isto cria uma oportunidade maravilhosa para a reutilização de software. Necessitamos 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 recurso é fornecido por gabaritos de classe em C++. 
Observação de engenharia de software 12.2 
_______ Os gabaritos de classe incentivam a reutilização de software, possibilitando que sejam instanciadas versões de classes genéricas para tipos espec(ficos. 
Gabaritos de classe são chamados de tipos parametrizados porque 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 específico. 
O programador que deseja produzir diversas classes gabarito simplesmente escreve uma definição de gabarito de classe. Toda vez que o programador necessita de uma nova instanciação específica para um tipo, o programador usa uma notação concisa, simples e o compilador escreve o código-fonte para a classe gabarito de que o programa- dor necessita. Um gabarito de classe Stack, por exemplo, poderia assim se tornar a base para criar muitas classes Stack (tais como “Stack de double”, “Stack de int”. “Stack de char”. “ Stack de Employee’. etc.) usadas em um programa. 
Note a definição do gabarito de classe Stack na Fig. 12.3. Parece com uma definição de classe convencional, a não ser pelo fato de ser precedida pelo cabeçalho (linha 6) 
template< class T > 
para especificar que esta é uma definição de gabarito de classe com o parâmetro de tipo T indicando o tipo da classe de Stack a ser criada. O programador não necessita usar especificamente o identificador T - qualquer identificador 
CAPÍTULO 12 - GABARITOS 685 
pode ser usado, O tipo de elemento a ser armazenado nessa Stack é mencionado somente genericamente como T, em todo o cabeçalho de classe de Stack e nas definições das funções membro. Mostraremos em breve como T torna-se associado com um tipo específico, tal como double ou int. Existem duas restrições para tipos de dados não-primitivos usados com esta Stack: eles devem ter um construtor default e devem suportar o operador de atribuição. Se um objeto da classe usada com esta Stack contém memória alocada dinamicamente, o operador de atribuição deve ser sobrecarregado para aquele tipo, como mostrado no Capítulo 8. 
1 II Fig. 12.3: tstackl.h 
2 // Gabarito de classe Stack 
3 #ifndef TSTACK1H 
4 #define TSTACK1H 
5 
6 template< class T > 
7 class Stack 
8 public: 
9 Stack( int = 10 ); // construtor default (tamanho da pilha 10) 
10 -‘Stack() { delete [] stackPtr; ) II destruidor 
11 bool push( const T& ); II insere um elemento na pilha 
12 bool pop( T& ); II retira um elemento da pilha 
13 private: 
14 int size; II quantidade de elementos na pilha 
15 int top; II posição do elemento do topo da pilha 
16 T *stackptr; IIponteiro para a pilha 
17 
18 bool isEmpty() const { return top == -1; } II funções 
19 bool isFull() const ( return top == size - 1; } II utilitárias 
20 
21 
22 // Construtor com tamanho default 10 
23 template< class T > 
24 Stack< T >::Stack( int s 
25 
26 size = s > O ? s : 10; 
27 top = -1; // inicialmente, Stack está vazia 
28 stackPtr = new T[ size ]; // aloca espaço para elementos 
29 } 
30 
31 II Insere uni elemento na pilha 
32 // retorna 1 se bem-sucedida, O caso contrário 
33 template< class T > 
34 bool Stack< T >::push( const T &pushValue 
35 { 
36 if ( ‘isFull() ) { 
37 stackPtr[ ++top ] pushValue; II coloca item em Stack 
38 return true; II push bem-sucedida 
39 1 
40 return false; II push malsucedida 
41 
42 
43 // Retira um elemento da pilha 
44 template< class T > 
45 bool Stack< T >: :pop( T &popValue 
46 { 
47 if ( !isEmpty() ) { 
48 popValue = stackPtr[ top- ]; II remove item de Stack 
49 return true; // pop bem-sucedida 
50 
Fig. 12.3 Demonstrando o gabarito de classe Stack - tstackl .h (parte 1 de 2). 
686 C++ COMO PROGRAMAR 
51 return false; II pop malsucedida 
52 } 
53 
54 #endif 
Fig. 12.3 Demonstrando o gabarito de coasse Stack - tstackl .h parte 2 ae 2). 
Agora consideremos o programa (função main) que testa o funcionamento do gabarito de classe Stack (ver a saída na Fig. 12.3). O programa de teste começa por instanciar o objeto doubleStack, de tamanho 5. Esse objeto é declarado como sendo da classe Stack< double> (pronunciado “Stack de double”). O compilador associa o tipo double com o parâmetro de tipo T no gabarito, para produzir o código-fonte para uma classe Stack do tipo double. Embora o programador não veja este código-fonte, ele é incluído no código-fonte e compilado. 
55 II Fig. 12.3: figl2O3.cpp 
56 II Programa de teste para o gabarito Stack 
57 #include <iostream> 
58 
59 using std: :cout; 
60 using std: :cin; 
61 using std: :endl; 
62 
63 #include “tstackl.h” 
64 
65 int main() 
66 { 
67 Stack< double > doubleStack( 5 ); 
68 double f 1.1; 
69 cout « “Inserindo elementos em doubleStack\n”; 
70 
71 while ( doubleStack.push( f ) ) { II sucesso: true retornado 
72 cout«f« ‘ 
73 f + 1.1; 
74 } 
75 
76 cout « “\nStack está cheia. Não pode inserir « f 
77 « “\n\nRetirando elementos de doubleStack\n”; 
78 
79 while ( doubleStack.pop( f ) ) II sucesso: true retornado 
80 cout « f « , 
81 
82 cout « “\nStack está vazia. Não pode retirar\n”; 
83 
84 Stack< int > intStack; 
85 inti=1; 
86 cout « “\nlnserindo elementos em intStack\n”; 
87 
88 while ( intStack.push( i ) ) { II sucesso: true retornado 
89 cout « i « 
90 
91 } 
92 
93 cout « “\nStack está cheia. Não pode inserir “ « i 
94 « “\n\nRetirando elementos de intStack\n”; 
95 
96 while ( intStack.pop( i ) ) II sucesso: true retornado 
97 cout«i« ‘ 
Fig. 12.3 Demonstrando o gabarito de classe Stack - figl2_03 . cpp (parte 1 de 2). 
CAPÍTULO 12 - GABARITOS 687 
98 
99 cout « \nStack está vazia. Não pode retirar\n”; 
100 return 0; 
101 
Inserindo elementos em doubleStack 1 
1.1 2.2 3.3 4.4 5.5 
Stack está cheia. Não pode inserir 6.6 
Retirando elementos de doubleStack 
5.5 4.4 3.3 2.2 1.1 
Stack está vazia. Não pode retirar 
Inserindo elementos em intStack 
1 2 3 4 5 6 7 8 9 10 
Stack está cheia. Não pode inserir 11 
Retirando elementos de intStack 
10 9 8 7 6 5 4 3 2 1 
Stack está vazia. Não pode retirar 
Fig. 12.3 Demonstrando o gabarito de classe Stack - figl2_03 . cpp (parte 2 de 2). 
O programa de teste então insere, sucessivamente, os valores double 1. 1,2.2,3.3,4.4 e 5.5 em doublestack. 
O laço de push termina quando o programa de teste tenta inserir um sexto valor em doubleStack (que já está 
cheia, porque foi criada para manter um máximo de cinco elementos). 
O programa de teste agora retira os cinco valores da pilha (note na Fig. 12.3 que os valores são retirados na ordem “último a entrar, primeiro a sair”), O programa de teste tenta retirar um sexto valor, mas doubleStack agora está vazio, assim o laço de pop termina. 
Em seguida, o programa de teste instancia a pilha de inteiros intStack com a declaração 
Stack< int > intStack; 
(pronunciada “intStack é um Stack de int”). Como nenhum tamanho foi especificado, o tamanho assume o valor default 10, conforme especificado no construtor default (linha 24). Uma vez mais, o programa de teste executa um laço de inserção de valores em intStack até que ela esteja cheia e, então, executa iterações de retirada de valores de intStack até que ela esteja vazia. Uma vez mais, os valores são retirados na ordem “último a entrar, primeiro a sair”. 
As definições de funções membro fora da classe começam, cada uma, com o cabeçalho (linha 23) 
template< class T > 
Então, cada definição se assemelha a uma definição convencional de função, exceto pelo fato de que o tipo de elemento de Stack é sempre listado genericamente como o parâmetro de tipo T. O operador de resolução de escopo binário é usado com o nome do gabarito de classe Stack< T > para amarrar cada definição de função membro ao escopo do gabarito de classe. Neste caso, o nome de classe é Stack< T >. Quando doubleStack é instanciado como sendo do tipo Stack< double >, o constructor de Stack usa new para criar um array de elementos do tipo double para representar a pilha (linha 28). O comando 
stackPtr = new T[ size ]; 
na definição do gabarito de classe de Stack é gerado pelo compilador na classe gabarito Stack< double > como 
stackPtr = new double[ size ]; 
688 C+÷ COMO PROGRAMAR 
Note que o código na função main da Fig. 12.3 é quase idêntico a ambas as manipulações de doubleStack na metade superior de main e as manipulações de intStack na metade inferior de main. Isto nos apresenta uma outra oportunidade para usar um gabarito de função. A Fig. 12.4 usa o gabarito de função testStack para executar as mesmas tarefas que main na Fig. 12.3 - insere uma série de valores em uma Stack< T > e retira os valores de uma Stack< T >. O gabarito de função testStack usa o parâmetro de tipo formal T para representar o tipo de dados armazenados na Stack< T >. O gabarito de função aceita quatro parâmetros - uma referência para um objeto do tipo Stack< T >, um valor do tipo T que será o primeiro valor inserido no Stack< T >, um valor do tipo T usado para incrementar os valores inseridos no Stack< T > e um string de caracteres do tipo const char 
* que representa o nome do objeto Stack< T > para fins de saída de dados. A função main, agora, simplesmente instancia um objeto do tipo Stack< double > chamado doubleStack e um objeto do tipo Stack< int > chamado intStack e usa estes objetos nas linhas 42 e 43 
testStack( doubleStack, 1.1, 1.1, ‘doubleStack’ ); 
testStack( intStack, 1, 1, “intStack” ); 
Note que a saída da Fig. 12.4 corresponde precisamente à saída da Fig. 12.3. 
1 II Fig. 12.4: figl2O4.cpp 
2 II Programa de teste para o gabarito Stack. 
3 II A função main usa um gabarito de função para 
4 // manipular objetos do tipo Stack< T >. 
5 #include <iostream> 
6 
7 using std: :cout; 
8 using std: :cin; 
9 using std: :endl; 
10 
#include “tstackl .h’ 
II Gabarito de função para 
template< class T > 
void testStack( 
Stack< T > &theStack, 
T value, 
T increment, 
const char *stackName 
cout « “\Inserindo elementos em « stackName « \I; 
while ( theStack.push( value ) ) { II sucesso: true retornado cout « value « 
value += increment; 
cout « ‘\nPilha está cheia. Não pode inserir « value 
« “\n\nRetirando elementos de « stackName « ‘\n’; 
while ( theStack.pop( value ) ) II sucesso: true retornado cout « value « 
cout « “\nPilha está vazia. Não pode retirar \n”; 
int main() 
manipular Stack< T > 
II referência ao Stack< T > 
// valor inicial a ser inserido 
// incremento para valores subseqüentes 
// nome do objeto Stack < T > 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
Fig. 12.4 Passando um objeto gabarito Stack para um gabarito de função (parte 1 de 2). 
CAPÍTULO 12 - GABARITOS 689 
39 Stack< double > doubleStack( 5 ); 
40 Stack< int > intStack; 
41 
42 testStack( doubleStack,1.1, 1.1, “doubleStack” ); 
43 testStack( intStack, 1, 1, “intStack” ); 
44 
45 return 0; 
46 } 
Inserindo elementos em doubleStack 
1.1 2.2 3.3 4.4 5.5 
Pilha está cheia. Não pode inserir 6.6 
Retirando elementos de doubleStack 
5.5 4.4 3.3 2.2 1.1 
Pilha está vazia. Não pode retirar 
Inserindo elementos em intStack 
1 2 3 4 5 6 7 8 9 10 
Pilha está cheia. Não pode inserir 11 
Retirando elementos de intStack 
10 9 8 7 6 5 4 3 2 1 
Pilha está vazia. Não pode retirar 
Fig. 12.4 Passando um objeto gabarito Stack para um gabarito de função (parte 2 de 2). 
12.5 Gabaritos de classe e parâmetros não-tipo 
O gabarito de classe Stack 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 não-tipo 
é tratado como const. Por exemplo, o cabeçalho do gabarito poderia ser modificado para aceitar um parâmetro 
int elements. como segue: 
template< class T, int elements > //note parâmetro não-tipo 
Então, um declaração tal como 
Stack< double, 100 > mostRecentSalesFigures; 
iria instanciar durante a compilação) uma classe gabarito Stack de 100 elementos chamaaa mostRecentSalesFigures de valores double: esta classe gabarito seria do tipo Stack< double, 100 
>. O cabeçalho de classe pode então conter um membro de dados private com uma declaração de array tal como 
T stackHolder[ elements ]; // array para guardar o conteúdo da pilha 
Dica de desempenho 12.2 
Quando é possívelfazê-lo, especificar o tamanho de uma classe contêiner (tal como uma classe array ou 
uma classe pilha) durante a compilação (possivelmente através de um parâmetro de gabarito não-tipo 
para o tamanho) elimina o overhead de criar o espaço dinamicamente com new durante a execução. 
Observação de engenharia de software 12.3 
Quando é possível fazê-lo, especificar o tamanho de um contêiner durante a compilação (possivelmente através de um parâmetro de gabarito não-tipo para o tamanho) evita a possibilidade de um erro potencialmente fatal durante a execução se new ficar impossibilitado de obter a memória necessária. 
690 C++ COMO PROGRAMAR 
Nos exercícios, será pedido a você para usar um parâmetro não-tipo para criar um gabarito para a classe Array desenvolvida no Capítulo 8, “Sobrecarga de operadores”. Este gabarito possibilitará a objetos Array serem instanciados com um número especificado de elementos de um tipo especificado durante a compilação, em vez de criar espaço dinamicamente para os objetos Array durante a execução. 
Uma classe para um tipo específico, o qual não corresponde a um gabarito de classe comum, pode ser fornecida para redefinir o gabarito da classe para aquele tipo. Por exemplo, um gabarito de classe Array pode ser usado para instanciar um array de qualquer tipo. O programador pode optar por assumir o controle do instanciamento da classe Array de um tipo específico, tal como Marciano. Isto é feito simplesmente formando a nova classe com um nome de classe Array< Marciano>. 
12.6 Gabaritos e herança 
Os gabaritos e a herança se relacionam de vários modos: 
• 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. 
12.7 Gabaritos e friends 
Vimos que funções e classes inteiras podem ser declaradas como friends de classes não-gabarito. Com gabaritos de classe, os tipos óbvios de friends podem ser declarados. A relação de friend pode ser estabelecida entre um gabarito de classe e uma função global, uma função membro de outra classe (possivelmente uma classe gabarito) ou até uma classe inteira (possivelmente uma classe gabarito). As notações exigidas para estabelecer estas relações de friends podem ser incômodas. 
Dentro de um gabarito de classe para a classe X que foi declarado com 
template< class T > class X 
uma declaração de friend da forma 
friend void f 10; 
torna a função fi 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 T > class X 
uma declaração de friend da forma 
friend void f2( X< T > & ); 
para um tipo particular T, tal como float, torna a função f2 (X< float> &) 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 da outra 
CAPÍTULO 12 - GABARITOS 691 
classe usando o nome de classe e o operador de resolução de escopo binário. Por exemplo, dentro de um gabarito de classe para a classe x que foi declarado com 
template< class T > class X 
uma declaração de friend da forma 
friend void A::f40; 
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 T > class X 
uma declaração de friend da forma 
frierid void C < T >: :f5( X< T > & ); 
para um tipo particular T, 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 T > 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 T > 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 T, tal como float, todos os membros de class Z< float > tornam-se friends da classe gabarito X< float >. 
12.8 Gabarítos e membros static 
E os membros de dados static ? Lembre que, com uma classe não-gabarito, uma cópia de um membro de dados static é compartilhada entre todos os objetos da classe e o membro de dados static deve ser inicializado em escopo de arquivo. 
Cada classe gabarito instanciada a partir de um gabarito de classe tem sua própria cópia de cada membro de dados static do gabarito de classe; todos os objetos daquela classe gabarito compartilham aquele membro de dados static. E como com membros de dados static de classes não-gabarito, membros de dados static de classes gabarito devem ser inicializados em escopo de arquivo. Cada classe gabarito obtém sua própria cópia das funções membro estáticas do gabarito de classe. 
692 C++ COMO PROGRAMAR 
• Gabaritos nos possibilitam especificar uma gama de funções relacionadas (sobrecarregadas) - chamadas de funções gabarito 
- ou uma gama de classes relacionadas - chamadas de classes gabarito. 
• Para usar gabaritos de função, o programador escreve uma única definição de gabarito de função. Com base nos tipos dos argumentos fornecidos em chamadas para esta função, C÷+ gera funções separadas para tratar de cada tipo de chamada apropriadamente. Elas são compiladas junto com o resto do código-fonte de um programa. 
• Todas as definições de gabarito de função começam com a palavra-chave template seguida por parâmetros de tipo formais para o gabarito de função incluso entre sinais de maior e menor (< e >); cada parâmetro formal deve ser precedido pela 
palavra-chave class (ou typename). A palavra-chave class (ou typename) é usada para especificar parâmetros de tipo de gabaritos de função significando “qualquer tipo primitivo ou um tipo definido pelo usuário”. 
• Os parâmetros de tipo formais de definições de gabaritos são usados para especificar os tipos dos parâmetros para a função, o tipo de retorno da função e para declarar variáveis na função. 
• O nome de um parâmetro de tipo formal pode ser usado somente uma vez na lista de parâmetros de um cabeçalho degabarito. Os nomes de parâmetro de tipo formais de gabaritos de funções não necessitam ser únicos. 
• Um gabarito de função pode ser ele próprio sobrecarregado de vários modos. Podemos fornecer outros gabaritos de função que especificam o mesmo nome de função, mas parâmetros de função diferentes. Um gabarito de função pode também ser 
sobrecarregado fornecendo-se outras funções não-gabarito com o mesmo nome de função, mas parâmetros de função diferentes. 
• Gabaritos de classe fornecem os meios para descrever uma classe genericamente e instanciar classes que são versões específicas desta classe genérica para um tipo. 
• Gabaritos de classe são chamados de tipos de parametrizados; eles requerem parâmetros de tipo para especificar como personalizar um gabarito de classe genérico para formar uma classe gabarito específica. 
• O programador que deseja usar classes gabarito escreve um gabarito de classe. Quando o programador necessita de uma nova classe para um tipo específico, o programador usa uma notação concisa e o compilador escreve o código-fonte para a classe gabarito. 
• Uma definição de gabarito de classe se parece com uma definição de classe convencional, a não ser que ela é precedida por template< class T > ( ou template< typeriaxne T >), para indicar que esta é uma definição de gabarito de classe 
com o parâmetro de tipo T indicando o tipo da classe a ser criada. O tipo T é mencionado ao longo do cabeçalho de classe e das definições de funções membro, como um nome de tipo genérico. 
• As definições de funções membro fora da classe começam cada uma com o cabeçalho template< class T > (ou template< typename T>) . Então, cada definição de função se assemelha a uma definição de função convencional, a não ser que os 
dados genéricos na classe são sempre listados genericamente como parâmetros do tipo T. O operador de resolução de escopo binário é usado com o nome do gabarito de classe para amarrar cada definição de função membro ao escopo do gabarito de 
classe, como em ClassName< T>. 
• É possível se usar parâmetros não-tipo no cabeçalho de um gabarito de classe. 
• Uma classe para um tipo específico pode ser fornecida para sobrescrever o gabarito de classe para aquele tipo. 
• 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. 
• Funções e classes inteiras podem ser declaradas como friends de classes não-gabarito. Com gabaritos de classe, os tipos óbvios de friends possíveis podem ser declarados. A relação de friend pode ser estabelecida entre um gabarito de classe 
e uma função global, uma função membro de outra classe (possivelmente uma classe gabarito) ou até uma classe inteira (possivelmente uma classe gabarito). 
• Cada classe gabarito instanciada de um gabarito de classe tem sua própria cópia de cada membro de dados static do gabarito de classe; todos os objetos daquela classe gabarito compartilham aquele membro de dados static. E, como com 
os membros de dados static de classes não-gabarito, membros de dados static de classes gabarito devem ser inicializados em escopo de arquivo. 
Resumo 
lii 
• Cada classe gabarito obtém uma cópia das funções membro static do gabarito de classe. 
CAPÍTULO 12 - GABARITOS 693 
Terminologia 
argumento de gabarito 
classe gabarito 
declaração de gabarito de função 
definição de gabarito de função 
friend de um gabarito 
função gabarito 
função membro de classe gabarito 
função membro static de um gabarito de classe função membro static de uma classe gabarito gabarito de classe 
gabarito de função 
membro de dados static de um gabarito de classe membro de dados static de uma classe gabarito 
Erros comuns de programação 
nome de gabarito 
nome de gabarito de classe 
palavra-chave class em um parâmetro de tipo de gabarito palavra-chave template 
parâmetro de gabarito 
parâmetro de tipo em um cabeçalho de gabarito parâmetro não-tipo em um cabeçalho de gabarito parâmetro de tipo formal em um cabeçalho de gabarito sinais de menor e maior (< e>) 
sobrecarregando uma função gabarito 
template<class T> 
tipo parametrizado 
typename 
12.1 Não colocar class (ou typenalne) antes de cada parâmetro formal de tipo de um gabarito de função. 
12.2 Se um gabarito é invocado com um tipo de classe definido pelo usuário e se esse gabarito usa operadores (como ==. <=, etc.) com objetos do tipo daquela classe, então aqueles operadores devem ser sobrecarregados! Esquecer de sobrecarregar tais operadores provoca erros, porque o compilador, naturalmente, ainda gera chamadas às funções operador sobrecarregado apropriadas, apesar do fato de que estas funções não estão presentes. 
12.3 O compilador executa um processo de correspondência para determinar qual função chamar quando uma função é invocada. Se nenhuma correspondência pode ser encontrada ou se o processo de correspondência produz correspondências múltiplas, é gerado um erro de compilação. 
Dicas de desempenho 
12.1 Gabaritos certamente oferecem os benefícios de reutilização de software. Mas tenha em mente que múltiplas cópias de funções gabarito e classes gabarito ainda são instanciadas em um programa, apesar do fato de que o gabarito é escrito somente uma vez. Estas cópias podem consumir memória considerável. 
12.2 Quando é possível fazê-lo, especificar o tamanho de uma classe contêiner (tal como uma classe array ou uma classe pilha) durante a compilação (possivelmente através de um parâmetro de gabarito não-tipo para o tamanho) elimina o overhead de criar o espaço dinamicamente com new durante a execução. 
Observações de engenharia de software 
12.1 Os gabaritos são um dos recursos mais poderosos de C++ para a reutilização de software. 
12.2 Os gabaritos de classe incentivam a reutilização de software, possibilitando que sejam instanciadas versões de classes genéricas para tipos específicos. 
12.3 Quando é possível fazê-lo, especificar o tamanho de um contêiner durante a compilação (possivelmente através de um parâmetro de gabarito não-tipo para o tamanho) evita a possibilidade de um erro potencialmente fatal durante a execução se new ficar impossibilitado de obter a memória necessária. 
Dica de teste e depura ção 
12.1 Gabaritos de função, como macros, possibilitam a reutilização de software. Mas, diferentemente de macros, gabaritos de função ajudam a eliminar muitos tipos de erros por causa do escrutínio da verificação de tipo completa de C++. 
Exercícios de auto-revisão 
12.1 Responda a cada um dos seguintes itens com verdadeiro ou falso. Para aqueles que são falsos, mostre por quê. 
a) Uma função friend de um gabarito de função deve ser uma função gabarito. 
b) Se várias classes gabarito são geradas a partir de um único gabarito de classe com um único membro de dados static, cada uma das classes gabarito compartilha uma cópia única do membro de dados static do gabarito de 
classe. 
694 C++ COMO PROGRAMAR 
c) Uma função gabarito pode ser sobrecarregada por outra função gabarito com o mesmo nome de função. 
d) O nome de um parâmetro de tipo formal pode ser usado somente uma vez na lista de parâmetros de tipo formais da 
definição do gabarito. Os nomes de parâmetros de tipo formais entre definições de gabaritos devem ser únicos. 
e) As palavras-chave class e typenaine. como usadas com um parâmetro de tipo de um gabarito, significam especificamente ‘qualquer tipo de classe definido pelo usuário”. 
12.2 Preencher os espaços em branco em cada um dos seguintes itens: 
a) Gabaritos nos possibilitam especificar, com um segmento de código único, uma gama inteira de funções relacionadas chamadas de ____________ ou uma gama inteira de classes relacionadas chamadas de ____________ 
b) Todas as definições de gabaritos de função começam com a palavra-chave seguida por uma lista de parâmetros formais do gabarito de função inclusos entre ___________________ 
c) As funções relacionadas geradas a partir de um gabarito de funçãotêm todas o mesmo nome, de modo que o compilador usa resolução de para invocar a função apropriada. 
d) Gabaritos de classes são também chamados de tipos 
e) O operador é usado com um nome de classe gabarito para amarrar cada definição de função membro ao escopo do gabarito de classe. 
f) Como com os membros de dados static de classes não-gabarito, membros de dados static de classes gabarito também devem ser inicializados em escopo de 
Respostas aos exercícios de auto-revisão 
12.1 a) Falso. Poderia ser uma função não-gabarito. b) Falso. Cada classe gabarito terá uma cópia do membro de dados static. c) Verdadeiro. d) Falso. Os nomes de parâmetro de tipo formais entre funções gabarito não precisam ser únicos. 
e) Falso. A palavra-chave class, neste contexto, também permite um parâmetro de tipo de um tipo primitivo. 
12.2 a) funções gabarito, classes gabarito. b) template, sinais de menor e maior (< e >). c) sobrecarga. d) parametrizados. e) resolução de escopo binário. f) arquivo. 
Exercícios 
12.3 Escreva um gabarito de função bubbleSort baseado no programa de classificação da Fig. 5.15. Escreva um programa de teste que lê da entrada, classifica e envia para a saída um array int e um array float. 
12.4 Sobrecarregue o gabarito de função printArray da Fig. 12.2 de forma que ele aceite dois parâmetros do tipo inteiro adicionais, quais sejam int lowSubscript e int highSubscript. Uma chamada a esta função imprimirá somente a parte indicada do array. Valide lowSubscript e highSubscript: se um deles estiver fora do intervalo ou se highSubscript for menor que ou igual a lowSubscript. a função sobrecarregada printArray deve retornar O; caso contrário, printArray deve retornar o número de elementos impressos. A seguir, modifique main para testar ambas as versões de printArray com os arrays a, b, e c. Não deixe de testar todos os recursos de ambas as versões de printArray. 
12.5 Sobrecarregue o gabarito de função printArray da Fig. 12.2 com uma versão não-gabarito que especificamente imprime um array de strings de caracteres em formato organizado. tabular. por colunas. 
12.6 Escreva um gabarito de função simples para a função predicado elgualA que compara seus dois argumentos com o operador de igualdade (==) e retorna true se eles forem iguais e false se eles não forem iguais. Use este gabarito de função em um programa que chama elgualA somente com diversos tipos primitivos. Agora, escreva, que uma versão separada do programa que chama e IgualA com um tipo de classe definido pelo usuário, mas não sobrecarrega o operador de igualdade. O que acontece quando você tenta executar este programa? Agora sobrecarregue o operador de igualdade (com a função operador operator==). O que acontece, agora, quando você tenta executar este programa? 
12.7 Use um parâmetro não-tipo numeroDeElementos e um parâmetro de tipo TipoDeElemento para ajudar a criar um gabarito para a classe Array que desenvolvemos no Capítulo 8, “Sobrecarga de operadores”. Este gabarito possibilitará a objetos Array serem instariciados com um número especificado de elementos de um tipo de elemento especificado durante a compilação. 
12.8 Escreva um programa com o gabarito de classe Array. O gabarito pode instanciar um Array de quaisquer tipos de 
elementos. Redefina o gabarito com uma definição específica para um Array de elementos float (class Array< float>). 
O programa de teste deve demonstrar a instanciação de um Array de ints a partir do gabarito e deve mostrar que uma tentativa 
de instanciar um Array de floats usa a definição fornecida em class Array< float >. 
1 
694 C++ COMO PROGRAMAR 
c) Uma função gabarito pode ser sobrecarregada por outra função gabarito com o mesmo nome de função. 
d) O nome de um parâmetro de tipo formal pode ser usado somente uma vez na lista de parâmetros de tipo formais da definição do gabarito. Os nomes de parâmetros de tipo formais entre definições de gabaritos devem ser únicos. 
e) As palavras-chave class e typename, como usadas com um parâmetro de tipo de um gabarito, significam especificamente “qualquer tipo de classe definido pelo usuário”. 
12.2 Preencher os espaços em branco em cada um dos seguintes itens: 
a) Gabaritos nos possibilitam especificar, com um segmento de código único, uma gama inteira de funções relacionadas chamadas de ____________ ou uma gama inteira de classes relacionadas chamadas de ____________ 
b) Todas as definições de gabaritos de função começam com a palavra-chave seguida por uma lista de parâmetros formais do gabarito de função inclusos entre ___________________ 
c) As funções relacionadas geradas a partir de um gabarito de função têm todas o mesmo nome, de modo que o compi lado usa resolução de para invocar a função apropriada. 
d) Gabaritos de classes são também chamados de tipos 
e) O operador é usado com um nome de classe gabarito para amarrar cada definição de função 
membro ao escopo do gabarito de classe. 
O Como com os membros de dados static de classes não-gabarito, membros de dados static de classes gabarito também devem ser inicializados em escopo de 
Respostas aos exercícios de auto-revisão 
12.1 a) Falso. Poderia ser uma função não-gabarito. b) Falso. Cada classe gabarito terá uma cópia do membro de dados static. c) Verdadeiro. d) Falso. Os nomes de parâmetro de tipo formais entre funções gabarito não precisam ser únicos. e) Falso. A palavra-chave class, neste contexto, também permite um parâmetro de tipo de um tipo primitivo. 
12.2 a) funções gabarito, classes gabarito. b) template, sinais de menor e maior (< e >). e) sobrecarga. d) parametrizados. e) resolução de escopo binário. f) arquivo. 
Exercícios 
12.3 Escreva um gabarito de função bubbleSort baseado no programa de classificação da Fig. 5.15. Escreva um programa de teste que lê da entrada, classifica e envia para a saída um array int e um array float. 
12.4 Sobrecarregue o gabarito de função printArray da Fig. 12.2 de forma que ele aceite dois parâmetros do tipo inteiro adicionais, quais sejam int lowSubscript e int highSubscript. Uma chamada a esta função imprimirá somente a parte indicada do array. Valide lowSubscript e highSubscript; se um deles estiver fora do intervalo ou se highSubscript for menor que ou igual a lowSubscript, a função sobrecarregada printArray deve retornar O; caso contrário. printArray deve retornar o número de elementos impressos. A seguir, modifique main para testar ambas as versões de printArray com os arrays a, b, e c. Não deixe de testar todos os recursos de ambas as versões de priritArray. 
12.5 Sobrecarregue o gabarito de função printArray da Fig. 12.2 com uma versão não-gabarito que especificamente imprime um array de strings de caracteres em formato organi7ado, tabular, por colunas. 
12.6 Escreva um gabarito de função simples para a função predicado elgualA que compara seus dois argumentos com o operador de igualdade (==) e retoma true se eles forem iguais e false se eles não forem iguais. Use este gabarito de função em um programa que chama elgualA somente com diversos tipos primitivos. Agora, escreva, que uma versão separada do programa que chama elgualA com um tipo de classe definido pelo usuário, mas não sobrecarrega o operador de igualdade. O que acontece quando você tenta executar este programa? Agora sobrecarregue o operador de igualdade (com a função operador operator==). O que acontece, agora, quando você tenta executar este programa? 
12.7 Use um parâmetro não-tipo numeroDeElemeritos e um parâmetro de tipo TipoDeElemento para ajudar a criar um gabarito para a classe Array que desenvolvemos no Capítulo 8, “Sobrecarga de operadores”. Este gabarito possibilitará a objetos Array serem instanciados com um número especificado de elementos de um tipo de elemento especificado durante a compilação. 
12.8 Escreva um programa com o gabarito de classe Array. O gabarito pode instanciar um Array de quaisquer tipos de 
elementos. Redefina o gabarito com uma definição específica para um Array de elementos float (class Array< float>). 
O programa de teste deve demonstrar a instanciaçãode um Array de ints a partir do gabarito e deve mostrar que uma tentativa 
de instanciar um Array de floats usa a definição fornecida em class Array< float >. 
CAPÍTULO 12 - GABARITOS 695 
12.9 Qual a diferença entre os termos “gabarito de função” e “função gabarito”? 
12.10 O que se parece mais como um estêncil - um gabarito de classe ou uma classe gabarito? Explique sua resposta. 
12.11 Qual é a relação entre gabaritos de função e sobrecarga? 
12.12 Por que você optaria por usar um gabarito de função em vez de uma macro? 
12.13 Que problema de desempenho pode resultar do uso de gabaritos de função e gabaritos de classe? 
12.14 O compilador executa um processo de correspondência para determinar qual função gabarito chamar quando uma função 
é invocada. Em que circunstâncias uma tentativa de fazer uma correspondência pode provocar um erro de compilação? 
12.15 Por que é apropriado chamar um gabarito de classe de um tipo parametrizado? 
12.16 Explique por que você poderia usar o comando 
Array< Employee > workerList( 100 ); 
em um programa em C++. 
12.17 Revise sua resposta para o Exercício 12.16. Agora, por que você poderia usar o comando 
Array< Empioyee > workerList; 
em um programa em C++? 
12.18 Explique o uso da seguinte notação em um programa em C++: 
template< ciass T > Array< T >: :Array( int s ) 
12.19 Por que você poderia tipicamente usar um parâmetro não-tipo com um gabarito de classe para um contêiner tal como um array ou uma pilha? 
12.20 Descreva como fornecer uma classe para um tipo específico para sobrescrever o gabarito de classe para aquele tipo. 
12.21 Descreva a relação entre gabaritos de classe e herança. 
12.22 Suponha que um gabarito de classe tem o cabeçalho 
tempiate< ciass Ti > ciass Ci 
Descreva os relacionamentos do tipo friend estabelecidos ao se colocar cada uma das seguintes declarações friend dentro deste cabeçalho de gabarito de classe. Identificadores começando com “f “são funções, identificadores começando com “C” são classes e identificadores começando com “T” podem representar quaisquer tipos (i.e., tipos primitivos ou tipos de classe). 
a) friend void f 1Q; 
b) friend void f2( Ci< Ti > & ) 
c) friend void C2::f4( ); 
d) friend void C3< Ti >: :f5( Ci< Ti > & ) 
e) friend ciass C5; 
f) friend class C6< Ti >; 
12.23 Suponha que o gabarito de classe Empregado tem um membro de dados static contagem. Suponha que tres classes gabarito são instanciadas a partir do gabarito de classe. Quantas cópias do membro de dados static existirão? Como será restringido o uso de cada um (se for o caso)?

Continue navegando