resueltos programacion poo polimorfismo orientada objetos multiple libro herencia ejercicios constructores codigo clases c++ com multiple-inheritance virtual-inheritance

programacion - C++ Herencia Virtual MĂșltiple vs. COM



programacion orientada a objetos c++ libro pdf (5)

Ahora está un poco anticuado, pero la mejor referencia que he encontrado que concierne a C ++ es el Modelo de Objeto Inside the C ++ de Lippman. Los detalles exactos de implementación pueden no coincidir con los resultados de su compilador, pero la comprensión que proporciona es extremadamente valiosa.

Alrededor de la página 96 hay una explicación de la herencia virtual y aborda específicamente el problema del diamante.

Dejaré que lea los detalles, pero básicamente el uso de la herencia virtual requiere una búsqueda en la tabla virtual para ubicar la clase base. Este no es el caso en herencia normal, donde la ubicación de la clase base se puede calcular en tiempo de compilación.

(La última vez que tomé el camino más fácil y simplemente recomendé un libro para responder a una pregunta de desbordamiento de pila, voté considerablemente, así que vamos a ver si eso sucede de nuevo ... :)

La red está repleta de explicaciones del "temido problema del diamante" . También lo es StackOverflow. Creo que entiendo un poco, pero no puedo traducir ese conocimiento en comprender algo similar pero diferente.

Mi pregunta comienza como una pregunta pura de C ++, pero la respuesta bien podría ramificarse en los detalles de MS-COM. La pregunta general sobre el problema va:

class Base { /* pure virtual stuff */ }; class Der1 : Base /* Non-virtual! */ { /* pure virtual stuff */ }; class Der2 : Base /* Non-virtual! */ { /* pure virtual stuff */ }; class Join : virtual Der1, virtual Der2 { /* implementation stuff */ }; class Join2 : Join { /* more implementation stuff + overides */ };

Esta no es la solución clásica de diamantes. ¿Qué hace exactamente "virtual" aquí?

Mi verdadero problema es tratar de entender una discusión en el lugar de nuestros amigos en CodeProject. Implica una clase personalizada para crear un contenedor transparente para el reproductor Flash.

Pensé que probaría este lugar por diversión. Resulta que la siguiente declaración bloquea tu aplicación, con la versión 10 del reproductor Flash.

class FlashContainerWnd: virtual public IOleClientSite, virtual public IOleInPlaceSiteWindowless, virtual public IOleInPlaceFrame, virtual public IStorage

La depuración muestra que al ingresar las implementaciones de funciones (QueryInterface, etc.), de diferentes personas que llaman, obtengo diferentes valores de "this" -pointer para diferentes llamadas. ¡Pero quitar "virtual" hace el truco! No se bloquea, y el mismo puntero "este".

Me gustaría entender claramente qué está pasando exactamente. Muchas gracias.

Saludos, Adam


Creo que el problema con su ejemplo COM es que al agregar la palabra clave virtual está diciendo que todas las interfaces IOle * comparten una implementación IU desconocida común. Para implementar esto, el compilador tiene que crear varias tablas v, por lo tanto, usted tiene diferentes ''valores'' dependiendo de la clase derivada.

COM requiere que cuando llame a IQueryInterface en un objeto para IUnknown, TODAS las interfaces expuestas por el objeto devuelvan el mismo IUnknown ... que esta implementación claramente rompe.

Sin la herencia virtual, cada IOle * nominalmente tiene su propia implementación IUnknown. Sin embargo, dado que IUnknown es una clase abstracta, y no tiene ningún almacenamiento, el compilador y todas las implementaciones IUnknown provienen de FlashContainerWnd, solo hay una única implementación.

(OK, entonces ese último bit parece débil ... quizás alguien con un mejor entendimiento de las reglas del lenguaje pueda explicarlo más claramente)


La herencia virtual en el primer ejemplo no hace nada. Apostaría a que compilan el mismo código si fueron eliminados.

La clase prácticamente heredada simplemente Der1 el compilador que debería fusionar versiones posteriores de Der1 o Der2 . Como solo uno de cada uno aparece en el árbol de herencia, no se hace nada. Los virtuales no tienen efecto en Base .

auto p = new Join2; static_cast<Base*>(static_cast<Der1*>(p)) != static_cast<Base*>(static_cast<Der2*>(p))

La herencia virtual solo afecta a la siguiente clase heredada, y solo a las instancias que se han descargado de forma virtual. Esto es retroactivo de lo que cabría esperar, pero es una limitación en la forma en que se compilan las clases.

class A {}; class B : virtual public A {}; class C : virtual public A {}; class D : public A {}; class E : virtual public A, public B, public C, public D {}; class F : public A, public B, public C, public D {}; F::A != F::B::A or F::C::A or F::D::A F::B::A == F::C::A F::D::A != F::B::A or F::C::A or F::A E::B::A == E::C::A == E::A E::D::A != E::B::A or E::C::A or E::D::A

Una de las razones por las que A debe marcarse virtual en C y B en lugar de E o F es que C y B necesitan saber que no deben llamar al constructor de A. Normalmente habrían inicializado cada una de sus copias. Cuando participan en la herencia de diamantes, no lo hacen. Pero no se puede recompilar B y C para no construir A. Eso significa que C y B tienen que saber de antemano para crear código de constructor donde no se llama al constructor de A.


Pensé que solo probaría tu ejemplo. Se me ocurrio:

#include "stdafx.h" #include <stdio.h> class Base { public: virtual void say_hi(const char* s)=0; }; class Der1 : public Base { public: virtual void d1()=0; }; class Der2 : public Base { public: virtual void d2()=0; }; class Join : virtual public Der1, virtual public Der2 // class Join : public Der1, public Der2 { public: virtual void say_hi(const char* s); virtual void d1(); virtual void d2(); }; class Join2 : public Join { virtual void d1(); }; void Join::say_hi(const char* s) { printf("Hi %s (%p)/n", s, this); } void Join::d1() {} void Join::d2() {} void Join2::d1() { } int _tmain(int argc, _TCHAR* argv[]) { Join2* j2 = new Join2(); Join* j = dynamic_cast<Join*>(j2); Der1* d1 = dynamic_cast<Der1*>(j2); Der2* d2 = dynamic_cast<Der2*>(j2); Base* b1 = dynamic_cast<Base*>(d1); Base* b2 = dynamic_cast<Base*>(d2); printf("j2: %p/n", j2); printf("j: %p/n", j); printf("d1: %p/n", d1); printf("d2: %p/n", d2); printf("b1: %p/n", b1); printf("b2: %p/n", b2); j2->say_hi("j2"); j->say_hi(" j"); d1->say_hi("d1"); d2->say_hi("d2"); b1->say_hi("b1"); b2->say_hi("b2"); return 0; }

Produce el siguiente resultado:

j2: 00376C10 j: 00376C10 d1: 00376C14 d2: 00376C18 b1: 00376C14 b2: 00376C18 Hi j2 (00376C10) Hi j (00376C10) Hi d1 (00376C10) Hi d2 (00376C10) Hi b1 (00376C10) Hi b2 (00376C10)

Por lo tanto, al convertir un Join2 en sus clases base, puede obtener punteros diferentes, pero el puntero pasado a say_hi () es siempre el mismo, más o menos como se esperaba.

Entonces, básicamente, no puedo reproducir tu problema, por lo que es un poco difícil responder a tu pregunta real.

Con respecto a wat "virtual", encontré el artículo sobre wikipedia esclarecedor, aunque eso también parece enfocarse en el problema del diamante


Como dice Caspin, tu primer ejemplo en realidad no hace nada útil. Sin embargo, lo que hará es agregar un vpointer para indicar a las clases derivadas dónde encontrar las clases heredadas.

Esto arregla todos los diamantes que ahora puedes crear (lo que no creas), pero como la estructura de la clase ya no es estática, no puedes usar static_cast en ella. No estoy familiarizado con la API involucrada, pero lo que Rob Walker dice sobre IUnkown puede estar relacionado con esto.

En resumen, la herencia normal se debe usar cuando necesite su propia clase de base, que no se debe compartir con las clases de "hermanos": (a es un contenedor, b, c, d son partes que tienen un contenedor, e combina estas partes (mal ejemplo, ¿por qué no usar la composición?))

a a a | | | b c d <-- b, c and d inherit a normally / | / e

Mientras que la herencia virtual es para cuando su clase base se debe compartir con ellos. (a es vehículo, b, c, d son diferentes especializaciones de vehículo, e combina esto)

a / | / b c d <-- b, c and d inherit a virtually / | / d