terça-feira, 24 de junho de 2008

Trabalhando com objetos da classe Fila<AnsiString> no Borland C++ Builder


Fila ADT
Uma fila é um tipo abstrato de dados (ADT: abstract data type) que pode ser implementado em C++ como mostra a classe a seguir.
Começando pela declaração da interface da classe:
// início do arquivo "Fila.h"
//---------------------------------------------------------------------------
#ifndef FilaH
#define FilaH
// interface da classe Fila<ADT>
template <class objectType>
class Fila
{
        private:
                class Node
                {
                        public:
                                objectType *objetoCorrente;
                                Node *nodeSucessor;
                        public:
                                Node( objectType* );    // construtor
                                ~Node( void );          // destrutor
                };
        private:
                Node *nodeInicial, *nodeFinal;
                int qtdNodes;
        public:
                Fila( void );                           // construtor
                ~Fila( void );                          // destrutor
                void Guardar( objectType* );
                objectType* Retirar( void );
                int Tamanho( void );
};
//---------------------------------------------------------------------------
// Implementação da classe Fila<ADT>
//---------------------------------------------------------------------------
#include "Fila.cpp"
//---------------------------------------------------------------------------
#endif
// fim do arquivo "Fila.h"
A seguir temos a implementação dos métodos:
// início do arquivo "Fila.cpp"
//---------------------------------------------------------------------------
#pragma hdrstop
//---------------------------------------------------------------------------
#pragma package(smart_init)
//---------------------------------------------------------------------------
// Atenção: RETIRAR A LINHA #include "Fila.h"
//---------------------------------------------------------------------------
// construtor da classe "Fila"
template <class objectType>
Fila<objectType> :: Fila( void )
{
        this->qtdNodes = 0;
        this->nodeInicial = NULL;
        this->nodeFinal = NULL;
}
// destrutor da classe "Fila"
template <class objectType>
Fila<objectType> :: ~Fila( void )
{
        Node *_nodeCorrente = this->nodeInicial,  *_nodeSucessor;
        while ( _nodeCorrente != NULL ) {
                // guardar ponteiro para node sucessor
                _nodeSucessor = _nodeCorrente->nodeSucessor;
                // destruir objeto corrente
                delete _nodeCorrente->objetoCorrente;
                // destruir node corrente
                delete _nodeCorrente;
                // posicionar no node sucessor
                _nodeCorrente = _nodeSucessor;
        }
}
// guardar um objeto na fila
template <class objectType>
void Fila<objectType> :: Guardar( objectType *__objetoCorrente )
{
        // instanciar um novo node
        Node *_nodeCorrente = new Node( __objetoCorrente );
        // verificar se a fila está vazia
        if ( this->qtdNodes == 0 )
        {
                // atualizar o node inicial da fila
                // para o node recem instanciado
                this->nodeInicial = _nodeCorrente;
        }
        else
        {
                // atualizar o node sucessor do final corrente da fila
                // para o node recem instanciado
                this->nodeFinal->nodeSucessor = _nodeCorrente;
        }
        // atualizar o novo node final da fila
        // para o node recem instanciado
        this->nodeFinal = _nodeCorrente;
        // incrementar qtd de nodes na fila
        this->qtdNodes ++;
}
// retirar um objeto da fila
template <class objectType>
objectType* Fila<objectType> :: Retirar( void )
{
        // guardar node inicial da fila
        Node *_nodeCorrente = this->nodeInicial;
        // obter ponteiro do objeto corrente
        objectType *_objetoCorrente = _nodeCorrente->objetoCorrente;
        // fixar novo início da fila no node sucessor
        this->nodeInicial = _nodeCorrente->nodeSucessor;
        // decrementar qtd de nodes na fila
        this->qtdNodes --;
        // se esvaziar, apontar node final para NULL
        if ( this->qtdNodes == 0 )
        {
                this->nodeFinal = NULL;
        }
        // deletar node corrente
        delete _nodeCorrente;
        // retornar ponteiro do objeto retirado do início da fila
        return ( _objetoCorrente );
}
// consultar a quantidade de objetos na fila
template <class objectType>
int Fila<objectType> :: Tamanho( void )
{
        return ( this->qtdNodes );
}
// construtor da classe "Node"
template <class objectType>
Fila<objectType> :: Node :: Node( objectType *__objetoCorrente )
{
        this->objetoCorrente = __objetoCorrente;
        this->nodeSucessor = NULL;
}
// destrutor da classe "Node"
template <class objectType>
Fila<objectType> :: Node :: ~Node( void )
{
}

// fim do arquivo "Fila.cpp"
Os conceitos básicos de uma fila determinam que os elementos que a formam devem ser organizados pela ordem de chegada, ou seja, o primeiro que chegar à fila será o primeiro a ser retirado dela (FIFO: first-in, first-out); os elementos podem ser representados por "nodes" (ou "nós") que guardam 2 ponteiros: um para a estrutura de dados que desejamos organizar e o outro para o "node" seguinte na ordem de chegada da fila.
Usando a Fila ADT em outros projetos
Como exemplo, foi escolhido um caso de uso de strings do tipo Ansi, mas que poderia ser de outro tipo de dados como float, int, double, etc, inclusive outro tipo ADT como Números Complexos ou Números Racionais.
A seguir temos trechos de códigos para o projeto de teste:
1) Incluir no projeto as definições da classe "Fila":
        #include "Fila.h"
2) Definir um ponteiro para um objeto da classe Fila<AnsiString>:
Fila<AnsiString> *minhasstrings;
3) Instanciar o objeto da classe Fila<AnsiString>:
this->minhasstrings = new Fila<AnsiString>();
4) Inserindo objetos AnsiString na fila:
        this->minhasstrings->Guardar( new AnsiString( "São Paulo" ) );
        this->minhasstrings->Guardar( new AnsiString( "Rio de Janeiro" ) );
        this->minhasstrings->Guardar( new AnsiString( "Salvador" ) );
        this->minhasstrings->Guardar( new AnsiString( "Recife" ) );
        this->minhasstrings->Guardar( new AnsiString( "Fortaleza" ) );
        this->minhasstrings->Guardar( new AnsiString( "Manaus" ) );

5) Retirando objetos AnsiString da fila:
        this->ListBox1->Clear() ;
        // deixar os ultimos da fila para testar funcionalidade do
        // método destrutor da Fila<AnsiString>

        while( this->minhasstrings->Tamanho() > 2 )
        {
                AnsiString *valor = this->minhasstrings->Retirar();
                this->ListBox1->Items->Add( (*valor) );
                delete valor;
        }

6) Destruir o objeto da classe Fila<AnsiString> e consequentemente realizar uma varredura e destruição dos possíveis nodes remanescentes, também destruindo os respectivos objetos do tipo AnsiString associados a estes nodes:
        delete this->minhasstrings;
7) Não esquecer de habilitar o CodeGuard para certificar se tudo correrá como previsto, pelo menos durante o desenvolvimento.
Até a próxima.       

segunda-feira, 23 de junho de 2008

Usando Dll no Borland C++ Builder

 

Recomendações para o desenvolvimento dos dois projetos isolados em pastas separadas:

 

1) Criar novo projeto: File -> New -> Other -> Dll Wizard

        Project1 -> ProjDll.bpr
        Unit1 -> DllMain.cpp

2) Acrescentar um Header File para a DLL: File -> New -> Other -> Header File

        File1.h -> DllMain.h

3) Incluir em DllMain.h a diretiva de compilador:

        #include "MinhaClasse.h"

4) Incluir em DllMain.cpp a diretiva de compilador:

        #include "DllMain.h"

5) Criar nova Unit: File -> New -> Unit

        Unit2.cpp -> MinhaClasse.cpp
        Unit2.h -> MinhaClasse.h

6) Declarar a classe em MinhaClasse.h com a especificação:

        class __declspec(dllexport) MinhaClasse
        {
            ...
        };

7) Gerar a DLL: Project -> Build ProjDLL

8) Depurar erros da DLL e prosseguir somente quando estiver tudo correto.

9) Criar novo projeto: File -> New -> Application

        Project1.bpr -> ProjDemo.bpr
        Project1.cpp -> ProjDemo.cpp
        Unit1.cpp -> DemoFormMain.cpp
        Unit1.h -> DemoFormMain.h

10) Copiar da pasta do projeto da DLL os arquivos:

        ProjDll.lib
        ProjDll.dll
        DllMain.h
        MinhaClasse.h

11) Acrescentar no ProjDemo: Project -> Add to Project...

        Selecionar aruivos do tipo Library (*.lib)
        Indicar o arquivo a ser adicionado ao projeto: ProjDll.lib

12) Acrescentar no DemoFormMain.h a diretiva de compilador:

        #include "DllMain.h"

14) Substituir a diretiva de compilador em MinhaClasse.h de/para:

        dllexport -> dllimport

15) A partir deste estágio, já podemos declarar variáveis do tipo MinhaClasse:

        MinhaClasse obj1MinhaClasse;
        MinhaClasse *obj2MinhaClasse;

16) Se a declaração for para objeto estático, usar:

            obj1MinhaClasse.Atributo
            obj1MinhaClasse.Método()

17) Se a declaração for para objeto dinâmico, usar:

            obj2MinhaClasse = new MinhaClasse();
            ...
            obj2MinhaClasse->Atributo
            obj2MinhaClasse->Método()
            ...
            delete obj2MinhaClasse

18) Atenção para o ciclo vida dos objetos instanciados:

        estaticamente -> escopo da função
        dinamicamente -> requer Delete antes do término da função/programa

Até a próxima.