A questão aqui é que o objeto X sofre de crise de identidade.
Para entender o problema pense em qual o resultado esperado quando executado o programa. Se você me fizesse esta pergunta a algum tempo atraz eu responderia de pronto:
"Eu espero que seja impresso a string '[C++] In base constructor I'm derived'".
Para minha surpresa (e de muitos, pode estar certo), quando executado o programa imprime:
"'[C++] In base constructor I'm base'.
Entendeu? Não? Eu explico melhor.
Uma vez que o método WhoAmI() foi definido como virtual os desenvolvedores esperam que a chamada a este método no construtor da classe base resolva para o método adequado, ou seja, se estivermos instanciando um objeto da classe base o método WhoAmI() desta classe deveria ser chamado; se estivermos instanciando um objeto da classe derived o método desta classe deveria ser executado. Contudo este não é o comportamento observado ao executar o programa. Na realidade observamos que o método da classe base será executado, não importa se estamos instanciando um objeto desta classe ou de uma subclasse (por exemplo a classe derived). Faça o teste.
Neste momento tenho certeza que já tem alguém dizendo:
"Mas que estúpido! Porque não implementam isto de forma correta?"
Vamos tentar analisar: O padrão C++ dita que um objeto deve ter os construtores de suas classes executados da classe mais base para a classe mais derivada; neste exemplo, quando o objeto x for instanciado os construtores serão executados na ordem:
base --> derived
[você não sabe o que é uma vtable? Vou emprestar uma frase feminina milenar:
"se você não sabe o que é uma vtable não sou eu quem vou lhe dizer";
brincadeira a parte, este é um assunto para outro post].
O problema é que neste ponto o (construtor da classe base) o construtor da classe derived ainda não foi executado e, assim sendo, a entrada do método WhoAmI() na vtable do objeto x ainda aponta para o método da classe base.
Somente após a execução do construtor da classe derived é que a vtable será corrida, ou seja, chamadas ao método WhoAmI() serão direcionados ao método desta classe ao invés da classe base. Faça um teste; simplesmente adicione a linha abaixo à função main() do programa de exemplo:
x.WhoAmI();
Observe que será impresso "derived", ou seja, neste ponto a vtable do objeto x já esta correta.Isto occorre porque objetos nascem como objetos da classe mais base e vão amadurecendo, tomando a identidade de cada uma das classes base deste objeto, a medida que seus respectivos construtores vão sendo executados.
Observe que este comportamento não é decorrente de limitações tecnologicas mas sim de uma descisão consciente para previnir problemas; imagine qual seria o resultado de executar um método virtual de uma classe cujo construtor ainda não tenha sido executado (sim, a ordem de execução dos construtores não pode ser modificada). Neste exemplo não teriamos problema algum, contudo, em qualquer classe útil, possivelmente teríamos variáveis não inicializadas sendo utilizadas (isto realmente não é o comportamento que você deseja pode ter certeza).
Inclusive em algumas linguagens (por exemplo C#) objetos não sofrem desta crise de identidade mas por outro lado podem acessar variáveis não inicializadas se não forem corretamente implementados.
Espero ter sido claro.
[+/-] mostrar/esconder este post