Menu
Coddy logo textTech

Templates de Classe

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 membros 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.

challenge icon

Desafio

Fácil

Vamos construir um sistema de armazenamento genérico usando templates de classe para criar um contêiner flexível que pode conter qualquer tipo de dado. Você organizará sua classe template em um arquivo de cabeçalho e demonstrará sua versatilidade com diferentes tipos em seu programa principal.

Você criará dois arquivos:

  • Storage.h: Defina um template de classe chamado Storage que atua como um contêiner simples para um único valor com algumas operações úteis:

    Seu template Storage deve ter:

    • Um membro privado para manter o valor armazenado
    • 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 retorna true se o valor for igual ao valor construído por padrão do tipo T, e false caso contrário

    Também crie um template de classe chamado Pair com dois parâmetros de tipo que armazena dois valores relacionados:

    • Membros privados para o primeiro e o segundo valores (de tipos potencialmente diferentes)
    • Um construtor que inicializa ambos os valores
    • Métodos getFirst() e getSecond() para recuperar cada valor
    • Um método display() que imprime: (<first>, <second>)
  • main.cpp: Leia quatro entradas (cada uma em uma linha separada):
    1. Um valor inteiro
    2. Um novo valor inteiro
    3. Um valor de string
    4. Um valor double

    Demonstre seus templates:

    1. Criando um Storage<int> com o primeiro inteiro, imprimindo: Int storage: <value>
    2. Atualizando-o com o segundo inteiro usando setValue(), depois imprimindo: Updated: <value>
    3. Criando um Storage<std::string> com a entrada de string, imprimindo: String storage: <value>
    4. Verificando se o armazenamento de string está vazio e imprimindo: Is empty: <true/false> (imprima true ou false)
    5. Criando um Pair<std::string, double> com os valores de string e double, depois chamando display()
    6. Criando um Pair<int, int> com ambas as entradas inteiras, depois chamando display()

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 o 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 múltiplos parâmetros de tipo.

Folha de consulta

Templates de classe permitem que você crie classes independentes de tipo. O compilador gera versões específicas para cada tipo que você utiliza.

Template de Classe Básico

template <typename T>
class Box {
    T value;
public:
    Box(T v) : value(v) {}
    T getValue() const { return value; }
    void setValue(T v) { value = v; }
};

Ao criar objetos a partir de templates de classe, você deve especificar explicitamente o tipo usando colchetes angulares:

Box<int> intBox(42);
Box<std::string> strBox("Hello");

Múltiplos Parâmetros de Tipo

Templates de classe podem ter múltiplos parâmetros de tipo:

template <typename T, typename U>
class Pair {
    T first;
    U second;
public:
    Pair(T f, U s) : first(f), second(s) {}
};

Parâmetros Não-Tipo

Templates também podem aceitar parâmetros que não são tipos, como inteiros:

template <typename T, int Size>
class FixedArray {
    T data[Size];
public:
    int size() const { return Size; }
};

FixedArray<double, 5> arr;  // Array of 5 doubles

Definindo Funções Membro Fora da Classe

Ao definir funções membro fora da classe, repita 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; }

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 entrada de string, imprimir: String storage: <value>
    
    // TODO: Verificar se o armazenamento de string está vazio, imprimir: Is empty: true/false
    
    // TODO: Criar Pair<std::string, double> com string e double, chamar display()
    
    // TODO: Criar Pair<int, int> com ambos os inteiros, chamar display()
    
    return 0;
}
quiz iconTeste seus conhecimentos

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