2009-06-24 9 views
5

tengo jerarquía de interfaces públicas como esto:¿Son incompatibles las implementaciones de herencia virtual C++ de los compiladores diferentes?

struct ISwitchable { 
    /* Obtain pointer to another implemented interface of the same instance. */ 
    virtual int switch(unsigned int interfaceId, void** pInstance) = 0; 
}; 
struct IFoo : public ISwitchable { /* Methods */ }; 
struct IBar : public ISwitchable { /* Methods */ }; 
struct IFooBar : public IFoo, public IBar { /* Methods */ }; 

clase que implementa IFooBar se coloca en dll junto con la función de fábrica. El código de cliente carga dll, usa la función de fábrica para crear una instancia de clase y la usa según las interfaces (se suministran como un archivo de encabezado).

esquema funciona bien con dll hecha por MSVC y código de cliente realizado por Borland C++ Builder 6.

introduzco la herencia virtual en jerarquía:

struct IFoo : public virtual ISwitchable { /* Methods */ }; 
struct IBar : public virtual ISwitchable { /* Methods */ }; 

Y cuando en la misma situación (DLL MSVC , client by Builder) el código del cliente solicita una instancia de la clase que él obtiene con messy vtable.

¿Existe alguna solución, excepto la reversión a la herencia ordinaria?

+0

No estoy seguro de por qué fue -1ed, parece una pregunta razonable. –

+0

¿Cómo se estropea exactamente el vtable? –

+3

No lo sé, pero espero que MSVC y Borland C++ Builder difieran en la implementación de elementos internos como vtable y estructura de objeto. ¡Incluso diría que fue una suerte que la versión no virtual esté funcionando! Como el estándar de C++ no define las implementaciones exactas, solo la forma en que C++ funciona para el programador es en el productor del compilador de C++ el que diseña las estructuras internas de los objetos. – mmmmmmmm

Respuesta

-2

Probablemente no responda a su pregunta ... Pero se recomienda encarecidamente NO usar herencia múltiple como lo hace (llamado "diamante temido").

+0

De hecho, la DDD es solo un problema cuando los objetos de la clase base múltiple contienen datos de miembros; si no lo hacen, no importa qué objeto se elija, ya que los métodos (tanto virtuales como simples) son todos iguales. El único problema al que te enfrentarás es que el compilador no te permitirá subir a una de estas bases sin un lanzamiento intermedio, es decir, necesitas escribir "(ISwitchable *) (IFoo *) pFooBar" - molesto, pero no de por vida- amenazante. –

+3

¿Por qué no? Mire lo que se hereda de forma múltiple: el equivalente en C++ de una interfaz Java. Debería ser seguro. –

+0

De hecho, mi respuesta fue incorrecta. Como se dijo en el primer comentario, los problemas con la herencia múltiple aumentan al acceder a los miembros de datos de la clase base. Por lo tanto, este problema no existe con las interfaces. Esta debilidad de C++ ha sido eliminada por el lenguaje java al aceptar herencia única pero múltiples implementaciones de interfaz. – dilig0

8

No creo que pueda contar con ninguna clase construida que sea compatible con los compiladores. ¿Borland afirma que puede cargar e interoperar con las clases creadas por MSVC? Si es así, parece que tienen un error. Hasta donde yo sé, nada sobre la estructura exacta de la VTable está en la especificación de C++, por lo que no se espera que funcione en todos los compiladores.

7

A diferencia de C, no existe un compilador cruzado ABI para C++: los compiladores pueden implementar la herencia virtual (e incluso la herencia ordinaria) de la forma que deseen.

El resultado es: no se garantiza que funcione llamar a las funciones de C++ en los compiladores. Sé que es feo, pero si quieres que tu DLL interactúe felizmente con múltiples compiladores, es mejor que proporciones un conjunto de funciones simples extern "C" y tablas de funciones manuales construidas manualmente.

Nota: Los compiladores que admiten la construcción de objetos COM (o tienen una opción para hacerlo) están más restringidos en sus diseños de objetos. (Sé que las versiones recientes de MSVC++ producen objetos que cumplen con COM, al menos en la mayoría de los casos, aunque no estoy seguro si la herencia virtual está cubierta)

+0

La forma en que COM lo maneja es haciendo que el objeto haga su propio molde dentro de QueryInterface. Obtienes VTable limpio y simple de QueryInterface para la interfaz que solicitaste; no se supone que debas enviar a otros, sigues llamando a QI. El cliente no conoce la clase concreta, solo tiene un puntero a la interfaz, no le importa que la clase concreta use la herencia virtual. –

+0

@Lou: Absolutamente, pero sigue siendo el caso que admitir COM significa que el compilador está obligado a diseñar su vtable como una matriz contigua de indicadores de función, con requisitos de alineación específicos (= sin relleno) y con entradas en un orden específico - - requisitos que no están estipulados por el estándar C++. –

+1

Correcto, pero solo obtienes una interfaz a la vez, lo que significa que la clase real concreta puede tener una tabla virtual compleja, pero se convierte al estilo COM a través del modelo que no puede ser una clase heredada de múltiples. Por lo tanto, los compiladores pueden distribuir los vtables heredados de la forma que quieran, siempre que cuando realice una conversión a una clase base específica, obtenga el más simple. –

0

Estoy receloso del argumento void**. El uso de punteros vacíos pierde información de tipo.

Si está trabajando con herencia múltiple, escriba la información puede ser importante. Consideremos:

class Foo { ... }; 
class Bar { ... }; 

class Both: public Foo, public Bar { ... }; 

Vamos a suponer que la disposición interna de una instancia de ambos es una instancia de Foo seguido de una instancia de la barra. Puedo pasar un Both * a un método esperando un Foo * sin problemas. También puedo pasar un Both * a un método que espera una barra *, siempre que ajuste el puntero para que apunte a la barra incrustada. Puedo hacer esto de manera confiable porque sé que estoy trabajando con un Ambos.

Ahora:

Foo *foo = new Both(...); 
Bar *bar = new Both(...); 

void *p = foo; 
void *q = bar; 

Both *both = (which) ? (Both*)p : (Both*)q; 

Así que: ¿cómo sé cómo ajustar bien p o q cuando se le asigno a "ambos"? No puedo porque la información de tipo se pierde al pasar por el puntero de vacío.

Una variante de este problema puede estar relacionada con los problemas que tiene.

Cuestiones relacionadas