Implementando Pilha com Lista Ligada em C++

por Fabio A. Mazzarino

No último post implementamos uma pilha usando lista ligada usando C, neste post vamos implementar a mesma pilha, mas utilizando orientação a objetos. O foco é na implementação correta da uma estrutura de dados, em primeiro lugar os dados, e depois o algoritmo que manipulam os dados.

Os dois posts em conjunto vão servir para ilustrar a importância da estrutura de dados sobre o algoritmo. No próximo post vamos discorrer um pouco mais sobre porque dar tanta importância aos dados e não ao algoritmo.

Vamos começar definindo a interface da classe, os dados e os métodos de acesso:

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();
};

Comparando com a versão em C, foi utilizado um template <class T>, comparado com um ponteiro void da outra versão. Essa prática facilita muito a vida do programador, que não precisa lidar com ponteiros para utilizar a classe.

Além disso utilizamos uma classe protegida dentro da classe principal, afinal o programador não precisa de detalhes de como o armazenamento é implementado. Esse é o chamado encapsulamento de dados e código, método que faz com que a utilização da classe fique mais simples, reduz a manutenção do código, e melhor a estabilidade do sistema.

Também implementamos os mesmos métodos implementados na versão C ANSI.

O código da declaração da classe está disponível no git.

Implementando os Métodos

Vamos implementar os métodos de acesso aos dados:

#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 como a quantidade de código necessária também é pequena. Mesmo com a burocracia necessária para declarar o template. O arquivo também está disponível no git.

Testando a Pilha

O programa para testar a pilha em C++ é bem mais compacto:

#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 é possível notar uma das vantagens do C++, não há necessidade de se preocupar com ponteiros, toda a alocação de memória é gerenciada pela classe CStack e pelo compilador. O código fica mais compacto e mais fácil de manter. Claro que o código também está disponível do git.

No próximo post vamos discutir as vantagens do desenvolvimento centrado nos dados.