13 de setembro de 2010

Sobre a chamada de funções virtuais no construtor de uma classe

O desenvolvimento de software feito na ABC71 é um processo contínuo pois nosso ERP é o mesmo produto em todos os nossos Clientes. Quero dizer com isso que novas manutenções nunca partem do zero, o que faz com que tenhamos que nos preocupar também com códigos fontes legados. Como é parte do meu trabalho pesquisar para fazer avançar o sistema e ao mesmo tempo garantir que permanece funcionando o que já existe, falo neste post sobre uma situação na qual esse embate esteve presente.

Imagine que você projetou uma classe base com algumas funções virtuais e heranças dessa classe que se beneficiam de tais funções. Com o passar do tempo, estas implementações se consolidam como parte do sistema, encorporando-se ao código legado. Surge, então, a necessidade de criar uma nova herança daquela classe base. Por causa dessa nova classe, percebe-se que parte do código existente no construtor da classe base precisa ser específico em cada herança já que ele prepara algumas estruturas que, embora estejam declaradas na classe base, apenas as heranças têm conhecimento de como fazer a preparação. No meu caso concreto, a classe base introduz tratamentos genéricos envolvendo acesso a banco de dados enquanto as heranças tratam questões específicas para bancos de dados distintos.

O primeiro impulso é o de criar uma nova função virtual na classe base e implementá-la nas heranças conforme a necessidade de cada uma delas. O problema é que a chamada teria que ser feita no construtor da classe base para que a alteração surtisse o efeito desejado ! E, nesse ponto, o que está em construção é a própria classe base, não existindo ainda uma instância da classe herdada, a qual estamos de fato tentando construir !

A figura abaixo ilustra a sequência de chamadas que é gerada pelo compilador para o código referente à construção da instância de uma classe:
Sequência de execução de funções na construção de instâncias

A imagem mostra duas classes com relação de herança entre si, TWClasseBase e TWHerancaA. O código do construtor da classe base chama uma função virtual - Initialize - que é reescrita na classe herdada.

Sempre que criamos a instância de uma classe, o processo de construção se dá de cima pra baixo. Isto é, primeiro é criada a instância da classe mais básica, seguindo sequencialmente a hierarquia até a chamada do construtor da classe que estamos realmente instanciando. Isto ocorre porque você pode usar em sua classe - mesmo no constructor - qualquer estrutura que tenha sido introduzida nas classes superiores, gerando a necessidade de que estas estruturas já estejam iniciadas quando o construtor da sua herança for chamado.

Observe na figura que no momento em que a função virtual Initialize precisa ser chamada, apenas a classe base existe. Portanto, a tabela de funções virtuais só tem a referência ao Initialize da classe base, ignorando completamente aquele que foi escrito para a classe herdada.

Voltando ao caso original... Para resolver em definitivo o problema, uma opção seria reprojetar as classes, contornando a limitação. No entanto, eu teria como efeito colateral a obrigação de modificar literalmente centenas de fontes.

Encontrei a solução numa classe especial, chamada TObject. Em Delphi, esta classe especial serve automaticamente de base para todas as demais classes. Em C++ Builder, esta classe também existe mas você tem que declarar explicitamente a herança para poder se beneficiar dos recursos dela. O recurso que usei é uma função virtual chamada AfterConstruction introduzida pelo TObject. A própria linguagem garante que esta função será chamada automaticamente logo após a execução de todos os construtores necessários. Então, passei a chamada da minha função virtual Initialize para dentro do AfterConstruction da minha classe base. Agora, o Initialize chamado é o correto, associado à classe que realmente é instanciada.
char __fastcall TWClasseBase::AfterConstruction (void)
{
/* equivale ao inherited do Delphi */
TObject::AfterConstruction();

Initilize();
}


Mais Informações
Classe TObject

2 comentários :

Anônimo disse...

A pergunta pode estar fora do assunto, mas o assunto está no Blog : que ferramenta você utiliza pra criar as figuras como a da seqüência do construtor acima? Grato

Luís Gustavo Fabbro disse...

Eu uso o Visio, da Microsoft. Ele é parte do Office mas é incluído apenas em algumas das versões (Professional e Premium, se não me engano).



A ABC71 é empresa parceira da Microsoft e, por causa disso, recebemos algumas licenças de produtos deles como parte do pacote - incluindo o Visio e o Office.

Postar um comentário

OBS: Os comentários enviados a este Blog são submetidos a moderação. Por isso, eles serão publicados somente após aprovação.

Observação: somente um membro deste blog pode postar um comentário.