Pergunta

Eu estou pouco confuso sobre vptr e representação de objetos na memória, e espero que você possa me ajudar a entender o assunto, melhor.

  1. Considere B herda A e ambos definem funções virtuais f().Desde que eu aprendi a representação de um objeto da classe B, em que a memória se parece com isso:[ vptr | A | B ] e o vtbl que vptr pontos a contém B::f().Também percebi que o elenco do objeto da B para A não faz nada, exceto ignorando o B parte no final do objeto.É verdade?Não que esse comportamento é errado?Queremos que o objeto do tipo A para executar A::f() método e não B::f().

  2. Há um número de vtables no sistema, como o número de aulas?

  3. Como é que um vtable de classe que herda de duas ou mais classes de aparência?Como o objeto de C ser representados na memória?

  4. Mesmo que a pergunta 3, mas com a herança virtual.

Foi útil?

Solução

O seguinte é verdadeiro para o GCC (e isso parece ser verdade para o LLVM link), mas também pode ser verdadeiro para o compilador que você estiver usando.Todos estes é dependente de implementação, e não é regida pelo padrão de C++.No entanto, o GCC gravar seus próprios binários padrão de documento, Itanium ABI.

Eu tentei explicar conceitos básicos de como tabelas virtuais são estabelecidas em mais palavras simples, como uma parte da minha artigo sobre a função virtual desempenho em C++, o que você pode achar úteis.Aqui estão as respostas para suas perguntas:

  1. Uma forma mais correcta para descrever representação interna do objeto é:

    | vptr | ======= | ======= |  <-- your object
           |----A----|         |
           |---------B---------|
    

    B contém sua classe base A, ele apenas acrescenta alguns de seus próprios membros, e após o seu fim.

    Fundição de B* para A* de fato, não faz nada, ele retorna o mesmo ponteiro, e vptr permanece o mesmo.Mas, em poucas palavras, funções virtuais não são sempre chamados através da vtable.Às vezes, eles são chamados assim como as outras funções.

    Veja explicação mais detalhada.Você deve distinguir duas formas de chamar a função de membro:

    A a, *aptr;
    a.func();         // the call to A::func() is precompiled!
    aptr->A::func();  // ditto
    aptr->func();     // calls virtual function through vtable.
                      // It may be a call to A::func() or B::func().
    

    A única coisa é que ele é conhecido em tempo de compilação como a função será chamada:através da vtable ou apenas vai ser um costume de chamar.E a coisa é que o tipo de uma carcaça de expressão é conhecida em tempo de compilação, e , portanto, o compilador escolhe a função direita em tempo de compilação.

    B b, *bptr;          
    static_cast<A>(b)::func(); //calls A::func, because the type
       // of static_cast<A>(b) is A!
    

    Ele nem mesmo olhar para dentro de vtable neste caso!

  2. Geralmente, não.Uma classe pode ter várias vtables se herda várias bases, cada uma com sua própria vtable.Tal conjunto de tabelas virtuais, formulários de uma "mesa virtual de grupo" (cf.3).

    Classe precisa também de um conjunto de construção vtables, corretamente distpatch funções virtuais ao construir as bases de um objeto complexo.Você pode ler mais em o padrão I vinculado.

  3. Aqui está um exemplo.Suponha C herda A e B, cada definição de classe virtual void func(), assim como a,b ou c função virtual relevantes para o seu nome.

    O C vai ter uma vtable grupo de dois vtables.Ele irá compartilhar uma vtable com A (vtable onde as próprias funções da atual classe de ir é chamada de "primária"), e uma vtable para B vai ser acrescentados:

    | C::func()   |   a()  |  c()  || C::func()  |   b()   |
    |---- vtable for A ----|        |---- vtable for B ----| 
    |--- "primary virtual table" --||- "secondary vtable" -|
    |-------------- virtual table group for C -------------|
    

    A representação do objeto na memória vai olhar quase da mesma maneira a sua vtable parece.Basta adicionar um vptr antes de cada vtable em um grupo, e você vai ter uma estimativa de como os dados são dispostos dentro do objeto.Você pode ler mais sobre isto no seção relevante do GCC binário padrão.

  4. Virtual bases (alguns deles) são estabelecidas no final da vtable grupo.Isso é feito porque cada classe deve ter apenas uma base virtual, e se eles tombaram "usual" vtables, em seguida, o compilador não podia voltar a usar partes de construído vtables para fazer com que aqueles de classes derivadas.Isto poderia levar a computação desnecessários e deslocamentos poderá diminuir o desempenho.

    Devido a tal posicionamento, o virtual bases de introduzir, nas suas vtables elementos adicionais: vcall o deslocamento (para obter o endereço de uma final overrider ao saltar do ponteiro para uma base virtual dentro de um objeto completo para o início da classe que substitui a função virtual) para cada função virtual definidos.Também cada base virtual adiciona vbase deslocamentos, que são inseridos vtable da classe derivada;eles permitem encontrar o local onde os dados de base virtual de começar (não pode ser pré-compilados desde o endereço real depende de hierarquia:virtual bases estão no final do objeto, e a mudança de início varia, dependendo de como muitos não-aulas virtuais, a atual classe herda.).

A trama, espero que eu não introduzir muito complexidade desnecessária.Em qualquer caso, você pode consultar o padrão original, ou de qualquer documento de seu próprio compilador.

Outras dicas

  1. O que parece correto para mim.Não é errado como se você estiver usando Um ponteiro, você só precisa que fornecem mais, talvez, B funções de implementações que estão disponíveis a partir de Uma vtable (pode haver vários vtable, dependendo do compilador e hierarquia de complexidade).
  2. Eu diria que sim, mas é a implementação de compilador dependente, então você realmente não tem que saber sobre ele.
  3. e 4.Leia mais longe.

Eu recomendaria a leitura Herança Múltipla Consideradas Úteis , é um artigo longo, mas ele torna as coisas mais claras sobre o assunto como ele explica em detalhes como a herança funciona em C++ (figuras links não funcionam, mas eles estão disponíveis na parte inferior da página).

  1. Se o objeto B herda de A, então a memória de representação para B será o seguinte:

    • ponteiro para a tabela virtual de Um
    • Um determinado variáveis/funções
    • ponteiro para a tabela virtual de B
    • B determinadas variáveis/funções/substituições

    Se você tem B* b = new B();(A)b->f (), em seguida:

    • se f foi declarada como uma função virtual, em seguida, a B a implementação é chamada porque b é do tipo B
    • se f não foi declarada como uma função virtual, em seguida, quando chamado, não haverá de pesquisa em que ele vtable para a correta implementação e A execução vai ser chamado.
  2. Cada objeto terá seu próprio vtable (não tome isso como certo, como eu tenho para investigação

  3. Dê uma olhada no este para um exemplo de vtable layour quando lidando com herança múltipla

  4. Ver este para uma discussão sobre o diamante de herança e a vtable representação

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top