virtuales tiempo sobreescribir qué puras poo polimorfismo metodo herencia funciones ejemplo ejecucion conoce como codigo clase abstracta c++ virtual multiple-inheritance diamond-problem

c++ - tiempo - Herencia múltiple y funciones virtuales puras.



sobreescribir metodo c++ (3)

El lenguaje habitual de C ++ es:

  • Herencia virtual pública para clases de interfaz
  • Herencia privada no virtual para clases de implementación

En este caso tendríamos:

struct interface_base { virtual void foo() = 0; }; struct interface : virtual public interface_base { virtual void bar() = 0; }; struct implementation_base : virtual public interface_base { void foo(); }; struct implementation : private implementation_base, virtual public interface { void bar(); };

En la implementation , la base virtual única de interface_base es:

  • heredado públicamente a través de la interface : implementation --public -> interface --public -> interface_base
  • heredado de forma privada a través de la base_de_aplicación: implementation --privado -> base_de implementation_base --public -> interface_base

Cuando el código del cliente hace una de estas derivadas para basar las conversiones:

  • derivado a las conversiones de puntero de base,
  • enlace de referencia de tipo base con un inicializador de tipo estático derivado,
  • acceso a miembros de clase base heredados a través de un lvalor de tipo estático derivado,

lo que importa es solo que hay al menos una ruta de herencia accesible desde la clase derivada al subobjeto de clase base dado; Otras rutas inaccesibles son simplemente ignoradas. Debido a que la herencia de la clase base es solo virtual aquí, solo hay un tema de la clase base, por lo que estas conversiones nunca son ambiguas.

Aquí, la conversión de la implementation a interface_base , siempre se puede realizar mediante el código del cliente a través de la interface ; el otro camino inaccesible no importa en absoluto. La base virtual de interface_base única se hereda públicamente de la implementation .

En muchos casos, las clases de implementación ( implementation , base de implementation_base ) se mantendrán ocultas del código del cliente: solo se mostrarán los punteros o referencias a las clases de interface ( interface , base de interface_base ).

El siguiente código:

struct interface_base { virtual void foo() = 0; }; struct interface : public interface_base { virtual void bar() = 0; }; struct implementation_base : public interface_base { void foo(); }; struct implementation : public implementation_base, public interface { void bar(); }; int main() { implementation x; }

No se compila con los siguientes errores:

test.cpp: In function ''int main()'': test.cpp:23:20: error: cannot declare variable ''x'' to be of abstract type ''implementation'' test.cpp:16:8: note: because the following virtual functions are pure within ''implementation'': test.cpp:3:18: note: virtual void interface_base::foo()

He jugado un poco con él y he descubierto que hacer que las herencias ''interface -> interface_base'' y ''Implementation_base -> interface_base'' sean virtuales, soluciona el problema, pero no entiendo por qué. ¿Alguien por favor puede explicar qué está pasando?

ps omití los destructores virtuales a propósito para hacer el código más corto. Por favor, no me digas que las ponga, ya lo sé :)


Para el caso de "resolver" el problema de la herencia de diamantes, las soluciones ofrecidas por bdonlan son válidas. Dicho esto, puedes evitar el problema del diamante con el diseño. ¿Por qué cada instancia de una clase dada debe considerarse como ambas clases? ¿Alguna vez vas a pasar este mismo objeto a una clase que dice algo como:

void ConsumeFood(Food *food); void ConsumeDrink(Drink *drink); class NutritionalConsumable { float calories() = 0; float GetNutritionalValue(NUTRITION_ID nutrition) = 0; }; class Drink : public NutritionalConsumable { void Sip() = 0; }; class Food : public NutritionalConsumable { void Chew() = 0; }; class Icecream : public Drink, virtual public Food {}; void ConsumeNutrition(NutritionalConsumable *consumable) { ConsumeFood(dynamic_cast<Food*>(food)); ConsumeDrink(dynamic_cast<Drink*>(drink)); } // Or moreso void ConsumeIcecream(Icecream *icecream) { ConsumeDrink(icecream); ConsumeFood(icecream); }

Seguramente sería mejor en este caso que Icecream solo implementara NutritionalConsumable y proporcionara un GetAsDrink() y GetAsFood() que devuelva un proxy, simplemente por el hecho de aparecer como comida o bebida. De lo contrario, eso sugiere que hay un método u objeto que acepta un Food pero de alguna manera quiere verlo más tarde como una Drink , que solo puede lograrse con un dynamic_cast , y no es necesario que tenga un diseño más apropiado.


Tienes dos clases base de interface_base en tu árbol de herencia. Esto significa que debe proporcionar dos implementaciones de foo() . Y llamar a cualquiera de ellos será realmente incómodo, ya que será necesario desambiguar varios lanzamientos. Esto generalmente no es lo que quieres.

Para resolver esto, usa la herencia virtual:

struct interface_base { virtual void foo() = 0; }; struct interface : virtual public interface_base { virtual void bar() = 0; }; struct implementation_base : virtual public interface_base { void foo(); }; struct implementation : public implementation_base, virtual public interface { void bar(); }; int main() { implementation x; }

Con la herencia virtual, solo se crea una instancia de la clase base en cuestión en la jerarquía de herencia para todas las menciones virtuales. Por lo tanto, solo hay un foo() , que se puede satisfacer con la implementation_base::foo() .

Para obtener más información, consulte esta pregunta anterior : las respuestas proporcionan algunos diagramas agradables para aclarar todo esto.