Templates de Classes
Parte da seção Programação Orientada a Objetos do Journey de C++ da Coddy. Lição 65 de 104.
Assim como os templates de função permitem que você escreva funções independentes de tipo, os templates de classe permitem que você crie classes independentes de tipo. É assim que containers como std::vector funcionam - você define a estrutura uma vez, e o compilador gera versões específicas para cada tipo que você utiliza.
template <typename T>
class Box {
T value;
public:
Box(T v) : value(v) {}
T getValue() const { return value; }
void setValue(T v) { value = v; }
};
int main() {
Box<int> intBox(42);
Box<std::string> strBox("Hello");
std::cout << intBox.getValue() << std::endl; // 42
std::cout << strBox.getValue() << std::endl; // Hello
}Ao contrário dos templates de função, você deve especificar explicitamente o tipo ao criar objetos a partir de templates de classe usando colchetes angulares. O compilador então gera uma definição de classe completa para esse tipo específico.
Templates de classe podem ter múltiplos parâmetros de tipo e até mesmo parâmetros que não são de tipo, como inteiros:
template <typename T, int Size>
class FixedArray {
T data[Size];
public:
T& operator[](int index) { return data[index]; }
int size() const { return Size; }
};
FixedArray<double, 5> arr; // Array de 5 doubles
arr[0] = 3.14;Ao definir funções-membro fora da classe, você deve repetir a declaração do template:
template <typename T>
class Container {
T* data;
public:
Container();
~Container();
};
template <typename T>
Container<T>::Container() : data(nullptr) {}
template <typename T>
Container<T>::~Container() { delete data; }Templates de classe formam a base da programação genérica em C++, permitindo que você escreva estruturas de dados reutilizáveis que funcionam com qualquer tipo.
Desafio
FácilVamos criar um sistema de armazenamento genérico usando templates de classe para criar um contêiner flexível que possa armazenar qualquer tipo de dado. Você organizará sua classe de template em um arquivo de cabeçalho e demonstrará sua versatilidade com diferentes tipos no seu programa principal.
Você criará dois arquivos:
Storage.h: Defina um template de classe chamadoStorageque funciona como um contêiner simples para um único valor, com algumas operações úteis:Seu template
Storagedeve ter:- Um membro privado para armazenar o valor
- Um construtor que recebe um valor inicial
- Um método
getValue()que retorna o valor armazenado - Um método
setValue()que atualiza o valor armazenado - Um método
isEmpty()que retornatruese o valor for igual ao valor construído por padrão do tipo T, efalsecaso contrário
Crie também um template de classe chamado
Paircom dois parâmetros de tipo, que armazena dois valores relacionados:- Membros privados para o primeiro e o segundo valores (que podem ser de tipos diferentes)
- Um construtor que inicializa ambos os valores
- Métodos
getFirst()egetSecond()para obter cada valor - Um método
display()que imprime:(<first>, <second>)
main.cpp: Leia quatro entradas (cada uma em uma linha separada):- Um valor inteiro
- Um novo valor inteiro
- Um valor de string
- Um valor double
Demonstre seus templates:
- Criando um
Storage<int>com o primeiro inteiro e imprimindo:Int storage: <value> - Atualizando-o com o segundo inteiro usando
setValue()e, em seguida, imprimindo:Updated: <value> - Criando um
Storage<std::string>com a entrada de string e imprimindo:String storage: <value> - Verificando se o armazenamento de string está vazio e imprimindo:
Is empty: <true/false>(imprimatrueoufalse) - Criando um
Pair<std::string, double>com os valores de string e double e, em seguida, chamandodisplay() - Criando um
Pair<int, int>com ambas as entradas inteiras e, em seguida, chamandodisplay()
Por exemplo, com as entradas 42, 100, Hello e 3.14:
Int storage: 42
Updated: 100
String storage: Hello
Is empty: false
(Hello, 3.14)
(42, 100)Observe como o mesmo template Storage funciona perfeitamente com inteiros e strings, e como Pair pode combinar diferentes tipos. O compilador gera definições de classe separadas para cada combinação de tipos que você usa. Lembre-se de usar a sintaxe template <typename T> para templates de tipo único e template <typename T, typename U> para templates com vários parâmetros de tipo.
Experimente você mesmo
#include <iostream>
#include <string>
#include "Storage.h"
using namespace std;
int main() {
// Ler entradas
int intValue1;
int intValue2;
string strValue;
double doubleValue;
cin >> intValue1;
cin >> intValue2;
cin >> strValue;
cin >> doubleValue;
// TODO: Criar Storage<int> com o primeiro inteiro e imprimir: Int storage: <value>
// TODO: Atualizar com o segundo inteiro usando setValue(), imprimir: Updated: <value>
// TODO: Criar Storage<std::string> com a string de entrada, imprimir: String storage: <value>
// TODO: Verificar se o storage de string está vazio, imprimir: Is empty: true/false
// TODO: Criar Pair<std::string, double> com a string e o double, chamar display()
// TODO: Criar Pair<int, int> com ambos os inteiros, chamar display()
return 0;
}Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos externosBuild e compilação em C++Arquivos de cabeçalho e arquivos-fonteNamespaces e escopoIntrodução à POO em C++Classes vs. objetosO ponteiro 'this'Métodos (funções membro)Atributos (membros de dados)Noções básicas de construtores e destruidoresRevisão - calculadora simples4Propriedades de Classe
Membros de Instância vs. EstáticosGetters e SettersFunções Membro ConstPalavra-chave MutableMétodos e Variáveis EstáticosFunções e Classes FriendRevisão - Gerenciador de Contas Bancárias7Herança
Herança básicaNíveis de acesso da herançaOrdem de chamada de construtor e destrutorSobrescrita de métodosFunções virtuais e VTableHerança múltiplaHerança virtualRevisão - Hierarquia de funcionários10Visão geral da STL
Visão geral e filosofia da STLContêineres da STLIteradoresAlgoritmos da STLFunctors e expressões lambdaRecapitulação - Frequência de palavras13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonFactory e Abstract FactoryPadrão BuilderPadrão ObserverPadrão Strategy2Gerenciamento de Memória
Memória Stack vs. HeapPonteiros e ReferênciasMemória Dinâmica (new/delete)Ponteiros Inteligentes em C++RAII em C++Revisão - Gerenciador de Array Dinâmico5Encapsulamento
Especificadores de acesso em C++Especificadores de acesso em profundidadeOcultação de informaçõesStruct vs ClasseClasses aninhadas e internasRecapitulação - Sistema de registros de alunos8Polimorfismo
Polimorfismo em compilação vs. execuçãoSobrecarga de funçõesFunções virtuais revisadasFunções virtuais purasClasses abstratasDesign de interfaces em C++Conversão dinâmica e RTTIRecapitulação — Calculadora de formas11Conceitos Avançados de OOP
Composição vs. HerançaMixins via CRTPIdioma PimplApagamento de TiposClasses Enum e Tipagem ForteTratamento de Exceções em OOPHierarquias de Exceções Personalizadas14Padrões de Projeto — Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositeRAII como Padrão3Construtores e Destrutores
Construtor PadrãoConstrutor ParametrizadoConstrutor de CópiaConstrutor de MovimentoListas de Inicialização do ConstrutorConstrutores DelegantesAprofundamento em DestrutoresRegra dos Três / Cinco / ZeroRevisão - Classe String6Sobrecarga de Operadores
Introdução à Sobrecarga de OperadoresSobrecarga de Operadores AritméticosSobrecarga de Operadores de ComparaçãoOperadores de FluxoSobrecarga do Operador de AtribuiçãoSobrecarga dos Operadores [] e ()Operadores de Conversão de TipoRecapitulação - Classe Matrix9Templates
Templates de FunçõesTemplates de ClassesEspecialização de TemplatesTemplates VariádicosNoções Básicas de SFINAE e Traits de TiposRecapitulação - Contêiner GenéricoPratique por conta própria: Compilador de C++ online