Domanda

La singola eredità è facile da implementare. Ad esempio, in C, l'ereditarietà può essere simulata come:

struct Base { int a; }
struct Descendant { Base parent; int b; }

Ma con ereditarietà multipla, il compilatore deve disporre più genitori all'interno della classe di nuova costruzione. Come viene fatto?

Il problema che vedo sorgere è: i genitori dovrebbero essere sistemati in AB o BA, o forse anche in altro modo? E poi, se faccio un cast:

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents;

Il compilatore deve considerare se modificare o meno il puntatore originale. Cose virtuali simili sono richieste con i virtuali.

È stato utile?

Soluzione

Il seguente documento del creatore di C ++ descrive una possibile implementazione di eredità multipla:

Ereditarietà multipla per C ++ - Bjarne Stroustrup

Altri suggerimenti

C'era questo piuttosto vecchio articolo MSDN su come è stato implementato in VC ++ .

  

E poi, se faccio un cast:

SecondBase base = (SecondBase *) object_with_base1_and_base2_parents;
     

Il compilatore deve considerare se modificare o meno il puntatore originale. Cose difficili simili con i virtuali.

Con l'eredità non virutale questo è meno complicato di quanto si possa pensare: nel momento in cui viene compilato il cast, il compilatore conosce l'esatto layout della classe derivata (dopo tutto, il compilatore ha fatto il layout). Di solito tutto ciò che accade è un offset fisso (che può essere zero per una delle classi base) viene aggiunto / sottratto dal puntatore della classe derivata.

Con l'eredità virutale è forse un po 'più complesso - può implicare afferrare un offset da un vtbl (o simile).

Il libro di Stan Lippman, " Inside the C ++ Object Model " ha ottime descrizioni di come questa roba potrebbe (e spesso effettivamente funziona)

I genitori sono disposti nell'ordine in cui sono stati specificati:

class Derived : A, B {} // A comes first, then B

class Derived : B, A {} // B comes first, then A

Il tuo secondo caso viene gestito in un modo specifico del compilatore. Un metodo comune consiste nell'utilizzare puntatori più grandi della dimensione del puntatore della piattaforma, per archiviare dati extra.

Questo è un problema interessante che in realtà non è specifico per C ++. Le cose diventano più complesse anche quando hai una lingua con invio multiplo e eredità multipla (ad es. CLOS).

Le persone hanno già notato che esistono diversi modi per affrontare il problema. Potresti trovare interessante leggere un po 'i Protocolli Meta-Object (MOP) in questo contesto ...

Dipende interamente dal compilatore, ma credo che sia generalmente fatto attraverso una struttura gerarchica di vtables.

Ho eseguito un semplice esperimento:

class BaseA { int a; };
class BaseB { int b; };
class Descendant : public BaseA, BaseB {};
int main() {
        Descendant d;
        BaseB * b = (BaseB*) &d;
        Descendant *d2 = (Descendant *) b;
        printf("Descendant: %p, casted BaseB: %p, casted back Descendant: %p\n", &d, b, d2);
}

L'output è:

Descendant: 0xbfc0e3e0, casted BaseB: 0xbfc0e3e4, casted back Descendant: 0xbfc0e3e0

È bene rendersi conto che il casting statico non significa sempre "cambiare il tipo senza toccare il contenuto". (Bene, quando i tipi di dati non si adattano l'un l'altro, allora ci sarà anche un'interferenza nel contenuto, ma è una situazione diversa IMO).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top