2011-11-16 25 views
5

estoy leyendo sobre la herencia y tengo un gran problema que no he podido resolver durante horas:herencia virtual Confusión

Dada una clase Bar es una clase con funciones virtual,

class Bar 
{ 
    virtual void Cook(); 
}; 

¿Cuál es la diferencia entre:

class Foo : public Bar 
{ 
    virtual void Cook(); 
}; 

y

class Foo : public virtual Bar 
{ 
    virtual void Cook(); 
}; 

? Las horas de búsqueda en Google y la lectura produjeron mucha información sobre sus usos, pero ninguno realmente me dice cuál es la diferencia entre los dos, y solo me confunde más.

+2

No voy a responder porque el tema no merece realmente un tratamiento tan superficial: pero sin 'virtual' cada clase que hereda de' Bar' tendrá su propia copia de 'Bar', con' virtual' la clase más derivada tendrá solo una copia de 'Bar'. –

+0

Probar: [esta búsqueda] (http://stackoverflow.com/search?q = virtual + inheritance +% 5Bc% 2B% 2B% 5D) –

+0

posible duplicado de [¿En la clase base virtual de C++?] (http://stackoverflow.com/questions/21558/in-c-virtual-base-class) –

Respuesta

4

La herencia virtual solo es relevante si las clases deben heredar de Foo.Si se defina lo siguiente:

class B {}; 
class L : virtual public B {}; 
class R : virtual public B {}; 
class D : public L, public R {}; 

A continuación, el objeto final únicamente incluya una copia de B, compartida por ambos L y R. Sin el virtual, un objeto de tipo D contendría dos copias de B, una en L, y una en R.

Existe un argumento de que toda la herencia debe ser virtual (porque en los casos en que hace una diferencia, eso es lo que desea más de el tiempo). En la práctica, sin embargo, la herencia virtual es costosa, y en la mayoría de los casos, no es necesario: en un sistema bien diseñado, la mayoría de las herencia será simplemente de una clase concreta heredando de uno o más "interfaces"; tal clase concreta generalmente no está diseñada para ser derivada de sí misma, por lo que no hay problema. Pero existen excepciones importantes : si, por ejemplo, define una interfaz, y luego extensiones a la interfaz, las extensiones deberían heredar virtualmente desde la interfaz base, ya que una implementación concreta podría querer implementar varias extensiones. O si está diseñando mixins, donde ciertas clases solo implementan parte de la interfaz, y la clase final hereda de varias de estas clases (una por parte de la interfaz ). Al final, el criteron en cuanto a si herede prácticamente o no, no es demasiado difícil:

  • si la herencia no es pública, es probable que no debe ser virtual (nunca he visto una excepción), de lo contrario

  • Si la clase no está diseñado para ser una clase base, no hay necesidad de herencia virtual, de lo contrario

  • la herencia debe ser virtual.

Hay algunas excepciones, pero las reglas anteriores a errar por el lado de seguridad; por lo general, es "correcto" heredar prácticamente incluso en los casos en que la herencia virtual no es necesaria.

Un punto final: una base virtual siempre debe ser inicializado por la clase más derivada, no la clase que hereda directamente (y declara que la herencia es virtual). En la práctica, sin embargo, esto no es un problema. Si observa los casos en los que la herencia virtual tiene sentido, es siempre un caso de herencia de una interfaz, que no contendrá datos , y por lo tanto tendrá (solo) un constructor predeterminado. Si te encuentras heredando virtualmente de clases con constructores que toman argumentos , es hora de hacer algunas preguntas serias sobre el diseño.

5

Funcionalidad sabia que no hay mucha diferencia entre las 2 versiones. Con el caso de la herencia virtual, cada implementación generalmente agrega un puntero (vptr like) (igual que en el caso de las funciones virtual). Que ayuda a evitar múltiples copias de la clase base generada debido a la herencia múltiple (el problema) diamond inheritance

Además, virtual herencia delegados el derecho de llamar al constructor de su clase base. Por ejemplo,

class Bar; 
class Foo : public virtual Bar 
class Other : public Foo // <--- one more level child class 

lo tanto, ahora Bar::Bar() serán llamados directamente desde Other::Other() y también se colocará en el primer lugar entre las otras clases base.

Esta delegación característica ayuda en la implementación de una funcionalidad final class (en Java) en C++ 03:

class Final { 
    Final() {} 
    friend class LastClass; 
}; 

class LastClass : virtual Final { // <--- 'LastClass' is not derivable 
... 
}; 

class Child : public LastClass { // <--- not possible to have object of 'Child' 
}; 
3

En este caso, no hay diferencia. herencia virtual está relacionado con casos compartir superclase subobjetos por clases derivadas

struct A 
{ 
    int a; 
}; 

struct B : public virtual A 
{ 
    int b; 
} 

struct C : public virtual A 
{ 
    int c; 
}; 

struct D : public B, public C 
{ 
}; 

Hay una sola copia de la variable miembro a en el caso de D; Si A no era una clase base virtual, habría dos subobjetos A en instancia de D.

+0

"_En este caso, no hay diferencia._" hasta que intente usar 'static_cast'! – curiousguy

0

La función virtual es una función que probablemente tendrá una implementación diferente en la clase derivada (aunque no es obligatorio).

En su último ejemplo es la herencia virtual. Imagina un caso en el que tienes dos clases (A y B) derivadas de una clase base (llamémosla 'Base'). Ahora imagine una tercera clase C derivada de A y B. Sin herencia virtual, la C contendría dos copias de 'Base'. Eso podría generar ambigüedad al compilar. Lo importante en la herencia virtual es que los parámetros para el constructor de la clase 'Base' (si los hay) DEBEN proporcionarse en la clase C, porque tales llamadas de A y B serán ignoradas.

+0

"_Puede generar ambigüedad al compilar._" esto ** dará lugar a una ambigüedad _iff_ que intente con 'C' IS-A' Base'. – curiousguy