c++ - Uso de la herencia virtual



oop inheritance multiple-inheritance (4)

Su primera regla excluye la class D en absoluto:

Para el polimorfismo dinámico, considere el uso de herencia única (jerarquía en forma de árbol), posiblemente con herencia múltiple de interfaces abstractas

Tus reglas están bien.

Entonces, ¿cuál es tu pregunta? ¿Qué queda ahora?

Necesito escribir una convención de codificación que será utilizada tanto por los novatos como por los desarrolladores experimentados de C ++. La regla sobre la herencia para el polimorfismo dinámico es la siguiente:

  • Para el polimorfismo dinámico, considere el uso de herencia única (jerarquía en forma de árbol), posiblemente con herencia múltiple de interfaces abstractas
  • para la herencia a lo largo de la jerarquía (clases base, etc.), de forma predeterminada , use la herencia pública
  • para la herencia de la interfaz abstracta, de forma predeterminada , use la herencia virtual pública

A esta regla le seguirá una información detallada sobre la implementación, posibles excepciones, etc.

Entonces, la pregunta: ¿Es esta regla deseable tanto para los novatos como para los desarrolladores de C ++ con experiencia? (Pros / contras, así como fuentes y enlaces son bienvenidos)

Los que veo son:

Pros:

  • Regla fácilmente utilizable por los novatos, sin restringir a los desarrolladores experimentados.
  • familiar para aquellos que ya están familiarizados con las interfaces de Java / .NET
  • esquiva los problemas relacionados con la herencia virtual de la implementación (ya que está reservada para interfaces abstractas), así como la herencia no virtual (posible ambigüedad al realizar la conversión a la clase de interfaz)

Contras:

  • ligero costo de rendimiento (velocidad al lanzar a la interfaz, tamaño de las tablas virtuales, punteros adicionales en la instancia de clase)

Nota: He leído las siguientes fuentes en línea:

Nota 2: El uso del nombre de "interfaz abstracta" se acuñó después del uso de Sutter & Alexandrescu en el ítem 36 de "Estándares de codificación C ++"

Este es un caso que debería funcionar (su equivalente en Java / C # usando interfaces simplemente funciona), pero eso no funciona en C ++ si la herencia de la interfaz no es virtual:

class A
{
   public :
      virtual ~A() = 0 {}
} ;

class B : public A {} ; // should have been virtual to avoid the error
class C : public A {} ; // should have been virtual to avoid the error

class D : public B, public C
{
   public : 
      virtual ~D() {}
} ;

void foo(A * c) {}
void bar(D * d)
{
   foo(d) ; // Error: ambiguous conversions from 'D *' to 'A *
}

Y sí, la conversión explícita para eliminar la ambigüedad es la solución incorrecta (la conversión explícita suele ser la solución incorrecta de todos modos).


El ejemplo de su herencia no funciona si no es virtual debido al clásico problema de herencia múltiple en c ++, el diamante de la muerte . Básicamente, si no especifica la herencia virtual, cada clase principal (B, C) tiene sus propios objetos A de base. Esto hace que todos los accesos a las funciones y variables no estáticas de la clase base (conversión y también suponiendo) sean ambiguos. No puedo imaginar una situación en la que puedas evitar esto en c ++.

Edición: Para el registro este es el código de trabajo:

class A
{
public :
    virtual ~A() {}
};

class B : virtual public A {};
class C : virtual public A {};

class D : virtual public B, virtual public C
{
public :
    virtual ~D() {}
};

void foo(A * c) {}
void bar(D * d)
{
    foo(d);
}

int main(void)
{
    D d;
    foo(&d);
    return 0;
}

¿Sabes que? Ya das toda la información importante en la pregunta. No veo nada que responder a nivel técnico. Y al parecer, nadie más vio ningún problema técnico significativo con lo que publicaste tampoco.

Sin embargo, responderé a tu audaz pregunta: , es adecuado tanto para newbs como para profesionales.

  • Newbs tiene algunas directrices técnicas útiles.
  • Los profesionales pueden hacer lo que quieran si pueden dar una razón, porque califica sus reglas con "considerar" y "por defecto", por lo que básicamente nadie más puede decir que debe hacerlo más o menos debido a las reglas de estilo, porque sus reglas 'frases ya permiten excepciones.

Para ser simple, el destructor virtual es destruir los recursos en un orden adecuado, cuando se elimina un puntero de clase base que apunta al objeto de clase derivado.

 #include<iostream>
 using namespace std;
 class B{
    public:
       B(){
          cout<<"B()\n";
       }
       virtual ~B(){ 
          cout<<"~B()\n";
       }
 };
 class D: public B{
    public:
       D(){
          cout<<"D()\n";
       }
       ~D(){
          cout<<"~D()\n";
       }
 };
 int main(){
    B *b = new D();
    delete b;
    return 0;
 }

OUTPUT:
B()
D()
~D()
~B()

==============
If you don't give ~B()  as virtual. then output would be 
B()
D()
~B()
where destruction of ~D() is not done which leads to leak




c++ oop inheritance multiple-inheritance