Question

L'héritage unique est facile à mettre en œuvre. Par exemple, en C, l'héritage peut être simulé comme suit:

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

Mais avec l'héritage multiple, le compilateur doit organiser plusieurs parents dans la classe nouvellement construite. Comment se fait-il?

Le problème que je vois se poser est le suivant: les parents devraient-ils être arrangés à AB ou BA, ou peut-être même autrement? Et puis, si je fais un casting:

SecondBase * base = (SecondBase *) &object_with_base1_and_base2_parents;

Le compilateur doit déterminer s'il convient de modifier ou non le pointeur d'origine. Il en va de même pour les logiciels virtuels.

Était-ce utile?

La solution

L'article suivant du créateur de C ++ décrit une implémentation possible de l'héritage multiple:

Héritage multiple pour C ++ - Bjarne Stroustrup

Autres conseils

Il y avait cet article assez ancien de MSDN sur son implémentation dans VC ++. .

  

Et puis, si je fais un casting:

SecondBase base = (SecondBase *) object_with_base1_and_base2_parents;
     

Le compilateur doit déterminer s'il convient de modifier ou non le pointeur d'origine. Même chose délicate avec les virtuels.

Avec un héritage non virtuel, ceci est moins compliqué que vous ne le pensez - au moment où la distribution est compilée, le compilateur connaît la présentation exacte de la classe dérivée (après tout, le compilateur a effectué la présentation). Habituellement, tout ce qui se produit est qu’un décalage fixe (qui peut être égal à zéro pour l’une des classes de base) est ajouté / soustrait du pointeur de la classe dérivée.

Avec l'héritage virtuel, il est peut-être un peu plus complexe. Cela peut impliquer de récupérer un offset d'un vtbl (ou similaire).

Le livre de Stan Lippman, "À l'intérieur du modèle objet C ++" a très bonnes descriptions de la façon dont ces choses pourraient (et souvent en réalité) fonctionner.

Les parents sont classés dans l'ordre indiqué:

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

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

Votre deuxième cas est traité de manière spécifique au compilateur. Une méthode courante consiste à utiliser des pointeurs plus grands que la taille du pointeur de la plate-forme pour stocker des données supplémentaires.

C’est un problème intéressant qui n’est pas spécifique à C ++. Les choses deviennent plus complexes aussi lorsque vous avez une langue avec plusieurs envois ainsi que plusieurs héritages (par exemple, CLOS).

Les gens ont déjà noté qu'il y avait différentes manières d'aborder le problème. Vous trouverez peut-être intéressant de lire un peu plus sur les protocoles de méta-objets (MOP) dans ce contexte ...

Cela revient entièrement au compilateur, mais je pense que cela se fait généralement via une structure hiérarchique de vtables.

J'ai effectué une expérience simple:

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);
}

La sortie est:

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

Il est bon de comprendre que la diffusion statique ne signifie pas toujours "changer de type sans toucher au contenu". (Eh bien, lorsque les types de données ne s’adaptent pas, il y aura également une interférence dans le contenu, mais la situation est différente, IMO.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top