Pergunta

Eu quero saber o que "classe base virtual" é e o que significa.

Deixe-me mostrar um exemplo:

class Foo
{
public:
    void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};
Foi útil?

Solução

Classes base virtuais, usadas em herança virtual, são uma forma de evitar que múltiplas "instâncias" de uma determinada classe apareçam em uma hierarquia de herança ao usar herança múltipla.

Considere o seguinte cenário:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

A hierarquia de classes acima resulta no “temido diamante” que se parece com isto:

  A
 / \
B   C
 \ /
  D

Uma instância de D será composta por B, que inclui A, e C, que também inclui A.Então você tem duas “instâncias” (na falta de uma expressão melhor) de A.

Quando você tem esse cenário, você tem a possibilidade de ambiguidade.O que acontece quando você faz isso:

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

A herança virtual existe para resolver esse problema.Ao especificar virtual ao herdar suas classes, você está dizendo ao compilador que deseja apenas uma única instância.

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

Isto significa que existe apenas uma “instância” de A incluída na hierarquia.Por isso

D d;
d.Foo(); // no longer ambiguous

Espero que ajude como um mini resumo.Para mais informações, leia esse e esse.Um bom exemplo também está disponível aqui.

Outras dicas

Sobre o layout da memória

Como observação lateral, o problema com o Dreaded Diamond é que a classe base está presente várias vezes.Então, com herança regular, você acredita ter:

  A
 / \
B   C
 \ /
  D

Mas no layout da memória, você tem:

A   A
|   |
B   C
 \ /
  D

Isso explica por que quando ligar D::foo(), você tem um problema de ambiguidade.Mas o real O problema surge quando você deseja usar uma variável de membro de A.Por exemplo, digamos que temos:

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

Quando você tentar acessar m_iValue de D, o compilador protestará, porque na hierarquia verá dois m_iValue, nenhum.E se você modificar um, digamos, B::m_iValue (esse é o A::m_iValue pai de B), C::m_iValue não será modificado (esse é o A::m_iValue pai de C).

É aqui que a herança virtual se torna útil, pois com ela você retornará a um verdadeiro layout de diamante, com não apenas um foo() método apenas, mas também um e apenas um m_iValue.

O que poderia dar errado?

Imagine:

  • A tem algum recurso básico.
  • B adiciona a ele algum tipo de conjunto interessante de dados (por exemplo)
  • C adiciona a ele algum recurso interessante, como um padrão de observador (por exemplo, em m_iValue).
  • D herda de B e C, e assim de A.

Com herança normal, modificando m_iValue de D é ambíguo e isso deve ser resolvido.Mesmo que seja, há dois m_iValues dentro D, então é melhor você lembrar disso e atualizar os dois ao mesmo tempo.

Com herança virtual, modificando m_iValue de D está tudo bem...Mas...Digamos que você tem D.Através de seu C interface, você anexou um observador.E através do seu B interface, você atualiza o array legal, que tem o efeito colateral de alterar diretamente m_iValue...

Como a mudança de m_iValue é feito diretamente (sem usar um método de acesso virtual), o observador "escuta" através C não será chamado, porque o código que implementa a escuta está em C, e B não sabe disso...

Conclusão

Se você tem um diamante em sua hierarquia, significa que você tem 95% de chance de ter feito algo errado com essa hierarquia.

Explicar a herança múltipla com bases virtuais requer conhecimento do modelo de objeto C++.E explicar o tópico com clareza é melhor feito em um artigo e não em uma caixa de comentários.

A melhor e legível explicação que encontrei e que resolveu todas as minhas dúvidas sobre este assunto foi este artigo: http://www.phpcompiler.org/articles/virtualinheritance.html

Você realmente não precisará ler mais nada sobre o assunto (a menos que seja um escritor de compiladores) depois de ler isso...

Uma classe base virtual é uma classe que não pode ser instanciada:Você não pode criar um objeto direto a partir dele.

Acho que você está confundindo duas coisas muito diferentes.Herança virtual não é a mesma coisa que uma classe abstrata.A herança virtual modifica o comportamento das chamadas de função;às vezes ele resolve chamadas de função que de outra forma seriam ambíguas, às vezes ele adia o tratamento de chamadas de função para uma classe diferente daquela que seria esperada em uma herança não virtual.

Gostaria de acrescentar aos gentis esclarecimentos de OJ.

A herança virtual não vem sem um preço.Como acontece com todas as coisas virtuais, você obtém um impacto no desempenho.Existe uma maneira de contornar esse impacto no desempenho que é possivelmente menos elegante.

Em vez de quebrar o diamante derivando virtualmente, você pode adicionar outra camada ao diamante, para obter algo assim:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

Nenhuma das classes herda virtualmente, todas herdam publicamente.As classes D21 e D22 ocultarão a função virtual f() que é ambígua para DD, talvez declarando a função privada.Cada um deles definiria uma função wrapper, f1() e f2() respectivamente, cada um chamando f() local de classe (privado), resolvendo assim os conflitos.A classe DD chama f1() se quiser D11::f() e f2() se quiser D12::f().Se você definir os wrappers embutidos, provavelmente obterá sobrecarga zero.

Claro, se você puder alterar D11 e D12, poderá fazer o mesmo truque dentro dessas classes, mas muitas vezes esse não é o caso.

Além do que já foi dito sobre herança(s) múltipla(s) e virtual(is), há um artigo muito interessante no Diário do Dr. Dobb: Herança múltipla considerada útil

Você está sendo um pouco confuso.Não sei se você está confundindo alguns conceitos.

Você não tem uma classe base virtual no seu OP.Você apenas tem uma classe base.

Você fez herança virtual.Isso geralmente é usado em herança múltipla para que várias classes derivadas usem os membros da classe base sem reproduzi-los.

Uma classe base com uma função virtual pura não pode ser instanciada.isso requer a sintaxe que Paulo aborda.Normalmente é usado para que as classes derivadas definam essas funções.

Não quero explicar mais nada sobre isso porque não entendi totalmente o que você está perguntando.

Isso significa que uma chamada para uma função virtual será encaminhada para a classe “certa”.

C++ Perguntas frequentes leves FTW.

Resumindo, é frequentemente usado em cenários de herança múltipla, onde uma hierarquia “diamante” é formada.A herança virtual quebrará a ambiguidade criada na classe inferior, quando você chamar a função nessa classe e a função precisar ser resolvida para a classe D1 ou D2 acima dessa classe inferior.Veja o Item de perguntas frequentes para um diagrama e detalhes.

Também é usado em delegação irmã, um recurso poderoso (embora não seja para os fracos de coração).Ver esse PERGUNTAS FREQUENTES.

Consulte também o Item 40 em Effective C++ 3ª edição (43 na 2ª edição).

Exemplo de uso executável de herança Diamond

Este exemplo mostra como usar uma classe base virtual no cenário típico:para resolver a herança de diamantes.

#include <cassert>

class A {
    public:
        A(){}
        A(int i) : i(i) {}
        int i;
        virtual int f() = 0;
        virtual int g() = 0;
        virtual int h() = 0;
};

class B : public virtual A {
    public:
        B(int j) : j(j) {}
        int j;
        virtual int f() { return this->i + this->j; }
};

class C : public virtual A {
    public:
        C(int k) : k(k) {}
        int k;
        virtual int g() { return this->i + this->k; }
};

class D : public B, public C {
    public:
        D(int i, int j, int k) : A(i), B(j), C(k) {}
        virtual int h() { return this->i + this->j + this->k; }
};

int main() {
    D d = D(1, 2, 4);
    assert(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}

As aulas virtuais são não o mesmo que herança virtual.Classes virtuais que você não pode instanciar, a herança virtual é algo totalmente diferente.

A Wikipedia descreve isso melhor do que eu. http://en.wikipedia.org/wiki/Virtual_inheritance

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