Templates em C++ – Parte 2: Classes Templates

por Fabio A. Mazzarino

No post anterior abordamos como fazer uma função template, neste post vamos verificar como implementar uma classe template.

Para o exemplo de classe template vamos utilizar um código muito parecido com um que já usamos anteriormente, uma pilha Last In First Out (LIFO), ou seja o último elemento que entrar é o primeiro elemento a sair.

Vamos ao código. Primeiro a declaração da classe:

template <class T>
class CStack {
    protected:
        class CNode {
            public:
                T data;
                CNode *prev;
                CNode(T adata, CNode *aprev) : data(adata), prev(aprev) { };
        };

        unsigned size;
        CNode *topnode;

    public:
        CStack() : size(0), topnode(nullptr) { };
        ~CStack();

        void push(T adata);
        T pop();
        T top();
        unsigned getSize();
};

Note a forma de fazer a declaração (template <class T>), muito parecida com a forma de declarar uma função template. Podemos, inclusive, utilizar diversas classes, tal qual no caso das funções templates.

E agora a implementação:

#include "CStack.h"
template<class T> CStack<T>::~CStack() {
    while (topnode) {
        CNode *delnode = topnode;
        topnode = topnode->prev;
        delete delnode;
    }
}
template<class T> void CStack<T>::push(T adata) {
    CNode *newnode = new CNode(adata, topnode);
    topnode = newnode;
    size++;
}
template<class T> T CStack<T>::pop() {
    if (!topnode)
        return T();
    T data = topnode->data;
    CNode *delnode = topnode;
    topnode = topnode->prev;
    delete delnode;
    size--;
    return data;
}
template<class T> T CStack<T>::top() {
    if (!topnode)
        return T();
    return topnode->data;
}
template<class T> unsigned CStack<T>::getSize() {
    return size;
}

Note a necessidade de declarar o template cada vez que se faz uma referência de escopo (template <class T>).

Por último, o código de teste:

#include <iostream>
#include "CStack.h"
int main() {
    CStack<int> stack;
    std::cout << "Pushing..." << std::endl;
    for (int ct = 10; ct--; ) {
        stack.push(ct);
        std::cout << ct << " ";
    }
    std::cout << std::endl << "Stack size: " << stack.getSize() << std::endl;
    std::cout << "Stack top: " << stack.top() << std::endl;
    std::cout << "Popping..." << std::endl;
    while (stack.getSize())
        std::cout << stack.pop() << " ";
    std::cout << std::endl << "Stack size: " << stack.getSize() << std::endl;
}

Aqui a diferença para a função template: é necessário definir o tipo do template na declaração do objeto, ou do ponteiro para o objeto.

Containers STL

STL é a abreviação de Standard Template Library, ou seja a biblioteca padrão de templates, e utiliza extensivamente templates. As regras de utilização são exatamente as mesmas, sendo necessário definir os tipos em tempo de compilação durante a declaração do objeto ou do ponteiro.