A maioria dos compiladores modernos de C/C++ permitem o overhiding de funções através das funções membro virtuais (ou funções virtuais). O modo de proporcionar as funções virtuais é comumente conhecido como método da tabela virtual, e é inclusive usado por outras linguagens tais como D, C#, Visual BASIC e Delphi, embora estas diferentes linguagens usem terminologias diferentes para seus modos de implementar o polimorfismo dinâmico.
Geralmente o método da tabela virtual é implementado usando alguma variante da seguinte técnica: Se o objeto instanciado tem uma ou mais funções virtuais, o compilador coloca dentro do objeto um ponteiro oculto ao programador, denominado virtual-pointer, ou apenas v-pointer.
Esse v-pointer aponta para uma tabela denominada virtual-table ou v-table. O compilador cria uma v-table para cada classe que faça uso de funções virtuais, sendo que cada função virtual, tem um ponteiro que aponta para sí como elemento da tabela v-table de sua classe. A v-table de uma classe é totalmente independente da quantidade de objetos instanciados desta classe.
Na imagem abaixo temos um exemplo de uma hierarquia de herança fazendo o uso de funções virtuais para obter polimorfismo através de linkagem dinâmica:
Durante a chamada de uma função virtual, o sistema de execução deferência o v-pointer do objeto, tendo então acesso a v-table da classe na memória, então ele escolhe o elemento da v-table correspondente à função membro virtual em questão e o deferência para ir para o endereço de memória onde o código desta função membro virtual se encontra, conseguindo "gerar" o polimorfismo desejado.
Se supormos que os v-pointers são do tipo v_ptr e que as classes chamam o v-pointer de _v_pointer, poderíamos ter uma organização semelhante a descrita abaixo, com as setas representando para quem os ponteiros apontam:
A uso de v-table e v-pointers porém possui um trade-off implícito, pois seu uso trás um custo extra no tocante ao espaço usado na memória, pois é necessário um ponteiro extra, por objeto instanciado que precise de linkagem dinâmica, para representar o v-pointer e um elemento extra na v-table de cada classe, para cada função virtual desta. Além de, no mínimo, uma deferência extra, em relação as funções não-virtuais, que nada mais fazem do que "pular" para um espaço na memória pré-determinado em tempo de compilação.
Conclusão
Logo, o uso de funções virtuais possui um acesso um pouco mais devagar e gasta mais espaço na memória, embora que as CPUs modernas e o crescente aumento das memórias tenham tornado esse custo extra relativamente insignificante.
Para finalizar, podemos ver está quantidade extra de ponteiros e a presença da virtual table na figura abaixo:
Em contrapartida, sabemos que o polimorfismo proporcionado pela linkagem estática não gera overhead algum, o trabalho extra fica totalmente a cargo do compilador. A escolha de qual polimorfismo deve ser implementado depende apenas das necessidades do programador.
Bibliografia
Artigo originalmente publicado em meu blog:
Pinguim Engenheiro