tiempo - virtual float c++
En C++, ¿qué es una clase base virtual? (10)
Una clase base virtual es una clase que no se puede crear una instancia: no se puede crear un objeto directo a partir de ella.
Creo que estás confundiendo dos cosas muy diferentes. La herencia virtual no es lo mismo que una clase abstracta. La herencia virtual modifica el comportamiento de las llamadas a funciones; a veces resuelve las llamadas a funciones que de otro modo serían ambiguas, a veces difiere el manejo de llamadas a una clase distinta de la que se esperaría en una herencia no virtual.
Quiero saber qué es una " clase base virtual " y qué significa.
Déjame mostrarte un ejemplo:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
Sobre el diseño de la memoria.
Como nota al margen, el problema con el diamante temido es que la clase base está presente varias veces. Así que con la herencia regular, usted cree que tiene:
A
/ /
B C
/ /
D
Pero en el diseño de la memoria, tienes:
A A
| |
B C
/ /
D
Esto explica por qué cuando llama a D::foo()
, tiene un problema de ambigüedad. Pero el verdadero problema viene cuando quieres usar una variable miembro de A
Por ejemplo, digamos que tenemos:
class A
{
public :
foo() ;
int m_iValue ;
} ;
Cuando intentes acceder a m_iValue
desde D
, el compilador protestará, porque en la jerarquía verá dos m_iValue
, no uno. Y si modifica uno, digamos, B::m_iValue
(que es el padre A::m_iValue
de B
), C::m_iValue
no se modificará (ese es el padre A::m_iValue
de C
).
Aquí es donde la herencia virtual es útil, ya que con ella, volverá a un verdadero diseño de diamante, no solo con un método foo()
, sino también con un solo m_iValue
.
¿Qué puede salir mal?
Imagina:
-
A
tiene alguna característica básica. -
B
agrega algún tipo de matriz genial de datos (por ejemplo) -
C
agrega algunas características geniales como un patrón de observador (por ejemplo, enm_iValue
). -
D
hereda deB
yC
, y por tanto deA
Con la herencia normal, modificar m_iValue
desde D
es ambiguo y esto debe resolverse. Incluso si es así, hay dos m_iValues
dentro de D
, así que mejor recuerda eso y actualiza los dos al mismo tiempo.
Con la herencia virtual, modificar m_iValue
desde D
está bien ... Pero ... Digamos que tienes D
A través de su interfaz C
, adjuntas un observador. Y a través de su interfaz B
, actualiza la matriz cool, que tiene el efecto secundario de cambiar directamente m_iValue
...
Como el cambio de m_iValue
se realiza directamente (sin usar un método de acceso virtual), no se llamará al observador "escuchando" a través de C
, porque el código que implementa la escucha está en C
, y B
no lo sabe. .
Conclusión
Si tiene un diamante en su jerarquía, significa que tiene un 95% de haber hecho algo mal con dicha jerarquía.
Además de lo que ya se ha dicho sobre herencia (es) múltiple y virtual, hay un artículo muy interesante en el Diario del Dr. Dobb: Se considera que la herencia múltiple es útil
Estás siendo un poco confuso. No sé si estás mezclando algunos conceptos.
No tienes una clase base virtual en tu OP. Sólo tienes una clase base.
Hiciste herencia virtual. Esto generalmente se usa en herencia múltiple, de modo que varias clases derivadas usan los miembros de la clase base sin reproducirlos.
Una clase base con una función virtual pura no se crea una instancia. esto requiere la sintaxis a la que llega Paul. Normalmente se usa para que las clases derivadas definan esas funciones.
No quiero explicar más sobre esto porque no entiendo totalmente lo que estás preguntando.
Explicar la herencia múltiple con bases virtuales requiere un conocimiento del modelo de objetos C ++. Y explicar el tema claramente se hace mejor en un artículo y no en un cuadro de comentarios.
La mejor explicación legible que encontré que resolvió todas mis dudas sobre este tema fue este artículo: http://www.phpcompiler.org/articles/virtualinheritance.html
Realmente no necesitarás leer nada más sobre el tema (a menos que seas un compilador) después de leer eso ...
Las clases base virtuales, utilizadas en la herencia virtual, son una forma de evitar que aparezcan varias "instancias" de una clase determinada en una jerarquía de herencia cuando se usa la herencia múltiple.
Considere el siguiente escenario:
class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};
La jerarquía de clases anterior da como resultado el "diamante temido" que se ve así:
A
/ /
B C
/ /
D
Una instancia de D estará formada por B, que incluye A, y C, que también incluye A. Así que tienes dos "instancias" (a falta de una mejor expresión) de A.
Cuando tienes este escenario, tienes la posibilidad de ambigüedad. ¿Qué pasa cuando haces esto?
D d;
d.Foo(); // is this B''s Foo() or C''s Foo() ??
La herencia virtual está ahí para resolver este problema. Cuando especifica virtual al heredar sus clases, le está diciendo al compilador que solo desea una única instancia.
class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
Esto significa que solo hay una "instancia" de A incluida en la jerarquía. Por lo tanto
D d;
d.Foo(); // no longer ambiguous
Espero que ayude como un mini resumen. Para más información, lea this y this . Un buen ejemplo también está disponible here .
Las clases virtuales no son lo mismo que la herencia virtual. Clases virtuales que no puede instanciar, la herencia virtual es algo completamente distinto.
Wikipedia lo describe mejor que yo. this
Me gustaría añadir a las aclaraciones de OJ.
La herencia virtual no viene sin un precio. Como con todas las cosas virtuales, obtienes un impacto de rendimiento. Hay una manera de evitar este golpe de rendimiento que es posiblemente menos elegante.
En lugar de romper el diamante derivando virtualmente, puedes agregar otra capa al diamante, para obtener algo como esto:
B
/ /
D11 D12
| |
D21 D22
/ /
DD
Ninguna de las clases hereda virtualmente, todas heredan públicamente. Las clases D21 y D22 ocultarán la función virtual f () que es ambigua para DD, tal vez declarando que la función es privada. Cada uno de ellos definió una función de envoltura, f1 () y f2 () respectivamente, cada una llamando a la clase local (privada) f (), resolviendo así los conflictos. La clase DD llama a f1 () si quiere D11 :: f () y f2 () si quiere D12 :: f (). Si define las envolturas en línea, probablemente obtendrá alrededor de cero gastos generales.
Por supuesto, si puede cambiar D11 y D12, entonces puede hacer el mismo truco dentro de estas clases, pero a menudo ese no es el caso.
Significa que una llamada a una función virtual se reenviará a la clase "correcta".
C ++ this FTW.
En resumen, a menudo se usa en escenarios de herencia múltiple, donde se forma una jerarquía de "diamante". La herencia virtual luego romperá la ambigüedad creada en la clase inferior, cuando llama a la función en esa clase y la función debe resolverse a la clase D1 o D2 por encima de esa clase inferior. Vea el ítem de preguntas frecuentes para un diagrama y detalles.
También se usa en la delegación hermana , una característica poderosa (aunque no para los débiles de corazón). Vea this preguntas frecuentes.
Ver también el artículo 40 en la 3ª edición de Effective C ++ (43 en la 2ª edición)
Herencia de diamante ejemplo de uso ejecutable
Este ejemplo muestra cómo usar una clase base virtual en el escenario típico: para resolver la herencia de diamante.
#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);
}