2009-06-24 13 views
5

Ho gerarchia di interfacce pubbliche in questo modo:Le implementazioni di ereditarietà virtuale C++ di diversi compilatori non sono compatibili?

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 */ }; 

classe che implementa IFooBar è inserito nel dll con la funzione di fabbrica. Il codice client carica dll, usa la funzione factory per creare un'istanza di classe e usarla secondo le interfacce (vengono fornite come file di intestazione).

schema funziona bene con dll fatta da MSVC e il codice cliente da parte di Borland C++ Builder 6.

introduco eredità virtuale in gerarchia:

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

E quando nella stessa situazione (DLL MSVC , client by Builder) codice client richiede istanza di classe che ottiene con vtable disordinato.

Esiste una soluzione ad eccezione del rollback sull'eredità ordinaria?

+0

Non sono sicuro del motivo per cui è stato -1, sembra una domanda ragionevole. –

+0

Come esattamente il vtable è incasinato? –

+3

Non lo so, ma mi aspetto che MSVC e Borland C++ Builder differiscano nell'implementazione di tali elementi interni come la struttura di oggetti e vtable. Direi anche che è stata solo la fortuna che la versione non virtuale sta funzionando! Poiché lo standard C++ non definisce le esatte implementazioni ma solo il modo in cui C++ lavora per il programmatore è sul produttore di compilatori C++ per progettare le strutture di oggetti interne. – mmmmmmmm

risposta

-2

Probabilmente non risponderà alla tua domanda ... Ma si consiglia vivamente di NON usare l'ereditarietà multipla come si fa (chiamato "diamante temuto").

+0

Infatti, il DDD è solo un problema quando gli oggetti di classe base multiple contengono dati dei membri; in caso contrario, non importa quale oggetto è scelto, poiché i metodi (sia virtuali che semplici) sono tutti uguali. L'unico problema che dovrai affrontare è che il compilatore non ti permetterà di andare su una di queste basi senza un cast intermedio, cioè devi scrivere "(ISwitchable *) (IFoo *) pFooBar" - fastidioso, ma non vitale- minaccioso. –

+3

Perché no? Guarda cosa viene moltiplicato ereditato: l'equivalente in C++ di un'interfaccia Java. Dovrebbe essere sicuro. –

+0

In effetti la mia risposta era sbagliata. Come detto nel primo commento, i problemi con l'ereditarietà multipla aumentano quando si accede ai membri dei dati della classe base. Quindi questo problema non esiste con le interfacce. Questa debolezza del C++ è stata eliminata dal linguaggio java accettando l'ereditarietà singola ma implementazioni di interfaccia multiple. – dilig0

8

Non pensavo che si potesse contare su classi costruite compatibili con i compilatori. Borland afferma di poter caricare e interoperare con le classi create da MSVC. Se è così, sembra che abbiano un bug. Per quanto ne so, nulla sulla struttura esatta del VTable è nelle specifiche del C++, quindi non è previsto che funzioni sui compilatori.

7

A differenza di C, non esiste un cross-compilatore ABI per C++ - i compilatori sono liberi di implementare l'ereditarietà virtuale (e persino l'ereditarietà ordinaria) in qualsiasi modo.

Il risultato è: non è garantito il funzionamento delle funzioni C++ tra i compilatori. So che è brutto, ma se vuoi che la tua DLL interagisca felicemente con più compilatori, probabilmente hai fornito meglio una serie di semplici funzioni extern "C" e tabelle di punti di funzione create manualmente.

Nota: I compilatori che supportano la creazione di oggetti COM (o che hanno un'opzione per farlo) sono più limitati nei loro layout di oggetti. (So ​​che le versioni recenti di MSVC++ producono oggetti compatibili con COM, almeno nella maggior parte dei casi - non sono sicuro se l'ereditarietà virtuale è coperta però.)

+0

Il modo in cui viene gestito da COM consiste nel fare in modo che l'oggetto esegua il proprio casting all'interno di QueryInterface. Ottieni VTable semplici e puliti da QueryInterface all'interfaccia richiesta - non devi trasmettere ad altri, continui a chiamare QI. Il client non conosce la classe concreta, ha solo un puntatore all'interfaccia - non gli importa che la classe concreta usi l'ereditarietà virtuale. –

+0

@Lou: Assolutamente, ma è sempre il caso che il supporto di COM significhi che il compilatore è obbligato a definire il suo vtable come un insieme contiguo di puntatori di funzione, con requisiti di allineamento specifici (= nessun riempimento) e voci in un ordine specifico - - requisiti che non sono stabiliti dallo standard C++. –

+1

Corretto, ma si ottiene solo un'interfaccia alla volta - il che significa che la classe concreta reale potrebbe avere un VTable complesso, ma viene convertita allo stile COM attraverso il cast che non può essere associato a una classe ereditata in più modi. Quindi, i compilatori potrebbero impaginare più vtables ereditati come vogliono, purché io lanci a una classe base specifica, ottengo quella semplice. –

0

Sono sospettoso dell'argomento void**. L'uso di puntatori void perde le informazioni sul tipo.

Se si lavora con ereditarietà multipla, digitare le informazioni può essere importante. Considerare:

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

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

Supponiamo che internamente il layout di un'Istanza sia un'istanza di Foo seguita da un'istanza di Barra. Posso passare entrambi * a un metodo che prevede un Foo * senza problemi. Posso anche passare un * ad un metodo che prevede una barra *, a condizione che aggiusti il ​​puntatore in modo che punti alla barra incorporata. Posso farlo in modo affidabile perché so che sto lavorando con entrambi.

Ora:

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

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

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

Quindi: come faccio a sapere come regolare sia p o q quando assegno a "entrambi"? Non posso perché le informazioni sul tipo vengono perse passando attraverso il puntatore del vuoto.

Una variante di questo problema potrebbe essere correlata ai problemi che si verificano.

Problemi correlati