As classes base e derivadas compartilham uma tabela virtual? Quando me candidatei a um emprego, alguns entrevistadores de empresas famosas da Internet disseram que sim com certeza. Embora eu tenha dito a eles que o valor do ponteiro apontando para a tabela virtual no objeto da classe derivada é diferente do valor do ponteiro apontando para a tabela virtual no objeto da classe base, então eles não devem compartilhar a mesma tabela virtual, o entrevistador ainda disse com certeza que eles compartilham a mesma tabela virtual. Então, fiquei um pouco confuso e vim aqui para encontrar a resposta.
Pode depender da implementação, mas pelo menos nem o gcc nem o clang deixam a classe base e as classes derivadas compartilharem vtable. Haverá uma vtable por classe (que é então compartilhada entre todas as instâncias da classe). Isso derrotaria o propósito de implementar polimorfismo de tempo de execução usando vtables se não houvesse uma vtable por classe.
Claro que é um problema de implementação, mas a menos que você desabilite o RTTI, as classes base e derivadas não podem ter vptrs idênticos, já que eles também são usados para
typeid
edynamic_cast
, e as classes simplesmente não se comportam da mesma forma.Para compartilhar uma vtable, então, eles teriam que ter vtable ptrs e typeinfo ptrs separados (uma sobrecarga de ponteiro adicional por objeto , o que seria uma péssima escolha de implementação) ou fazer com que o vptr apontasse para um objeto typeinfo que é por classe, e então fazer com que ele contivesse o ponteiro para uma vtable compartilhada, o que significaria outro nível de indireção para chamadas de funções virtuais, o que é outra péssima escolha de implementação.
Nenhuma implementação sensata compartilhará vtables.
Este é um lugar onde os detalhes da língua inglesa importam, e falantes não nativos podem sofrer aqui. (No Staging Ground, ficou bem claro que o autor não é nativo. Felizmente, a questão foi esclarecida.) Talvez tenha havido alguma falha de comunicação durante a entrevista? Em particular, estou olhando para "no objeto da classe base", que não está bem formulado. Quando a formulação está um pouco errada, as pessoas tendem a presumir uma correção. No entanto, neste caso, vejo duas correções possíveis, que infelizmente levam a duas questões bem diferentes.
Se você mudar "the" para "a", então a comparação é entre o ponteiro em um objeto de classe derivada e aquele em um objeto de classe base (não um subobjeto). Dessa perspectiva, a questão é se uma classe base e uma classe derivada podem ou não compartilhar uma tabela de função virtual. Ou seja, pode haver uma vtable para ambas as classes?
Se você alterar "objeto" para "subobjeto", a comparação será sobre dois ponteiros em um único objeto. Cada objeto de classe derivada contém um subobjeto de classe base. O subobjeto de classe base precisa conter um ponteiro para uma vtable para que funções virtuais possam ser invocadas por meio de um ponteiro para a classe base. Da mesma forma, o objeto de classe derivada também deve conter um ponteiro para uma vtable. Dessa perspectiva, a questão é se esses dois ponteiros devem ou não ter o mesmo valor. Ou seja, existe uma vtable para esse objeto?
Classes e (múltiplas) vtables
Embora a implementação de vtables não seja padronizada, normalmente cada classe terá sua própria tabela de funções virtuais. A classe base terá uma, e a classe derivada terá uma diferente. Normalmente, haverá duas vtables, uma para cada classe.
Teoricamente, porém, isso não precisa ser o caso. Se a classe derivada não sobrescrever nenhuma função, as vtables para as classes base e derivada podem ser idênticas. Além disso, se as informações do tipo de tempo de execução forem suprimidas (o que pode limitar as capacidades de
dynamic_cast
), é plausível que o compilador possa otimizar essas duas tabelas em uma.Tenha em mente que a teoria é uma otimização para um caso bastante especial. E não sei se ela é realmente implementada. Então, é razoável concluir que classes diferentes têm vtables diferentes, pelo menos em qualquer caso em que isso importe.
Um objeto e sua (uma) vtable
Dentro de um objeto, uma classe derivada frequentemente usa o ponteiro de tabela virtual que já está presente no subobjeto da classe base. Devido ao arranjo inteligente da tabela, a classe base pode olhar para a tabela para uma classe derivada e "ver" apenas a parte que se aplica à classe base. (Isso é semelhante a como um ponteiro para base pode apontar para um objeto derivado e "ver" apenas o subobjeto da classe base.) Neste cenário, os valores dos vpointers são os mesmos porque há apenas um vpointer.
Em casos mais complexos, pode haver mais de um ponteiro de tabela virtual em um objeto, especialmente no caso de herança múltipla (ou seja, uma classe com várias classes base, em oposição a uma classe cuja classe base tem uma classe base). Se houver várias classes base com funções virtuais, cada uma precisa de seu próprio ponteiro de tabela virtual. Além disso, é praticamente impossível que esses vpointers tenham o mesmo valor, pois cada classe base determina independentemente como sua vtable é estruturada. Portanto, o vpointer da classe derivada não pode ter o mesmo valor que cada vpointer da classe base. (Ele corresponderá a um deles, no entanto, pelo menos nas implementações que conheço.)
Entretanto, mesmo que os vpointers das classes base sejam diferentes, ainda há apenas uma vtable para o objeto. Pense na classe derivada vtable como consistindo de funções da classe base 1, seguidas por funções da classe base 2, etc. Cada subobjeto da classe base teria um vpointer para sua porção da tabela da classe derivada. Então, em princípio, os vpointers apontam para a mesma tabela, apenas com deslocamentos diferentes.
Em resumo, para um dado objeto, há apenas uma tabela de funções virtuais. A classe derivada e uma de suas classes base concordarão com o endereço desta tabela. Se houver outras classes base, seus vpointers serão deslocados daquele da classe derivada, mas ainda apontarão para a mesma tabela.
Se você não tiver substituído um único método, as tabelas serão idênticas e, portanto, compartilhadas.
Se você tiver substituído qualquer coisa, mas adicionado mais métodos virtuais na classe derivada, então a classe base poderia usar o início da vtable das classes derivadas. E tudo isso somente se a vtable for usada somente para despacho de método.
Caso contrário, as vtables devem ser diferentes.