|   | 
        
            
                 
                 
                  
                Écrivez
                à AILES ! | 
             
            
                 
                 
                  
                Retour
                vers programmation | 
             
            
                 
                 
                  
                Retour
                vers les questions C++ | 
             
         
         | 
          | 
        
            
                 
                Questionnaire C++ 
                Héritage multiple 
                [AnnotatedC++],
                pp.198 et suiv. 
                 | 
             
            
                 
                L'héritage multiple provoque, de nos
                jours (1999), une réaction de plus en plus
                timide : on trouve cela "dangereux",
                "risqué", voir même "preuve
                d'une mauvaise modélisation"!... 
                Java a depuis longtemps tranché et en a purement
                et simplement interdit l'usage, lui préférant
                "l'implémentation d'interfaces
                multiples". 
                Pourquoi donc ? Le petit questionnaire suivant va
                vous le démontrer : gérer en mémoire des
                instances issues d'un héritage multiple, cela
                n'a rien d'évident. | 
             
         
        A
        quoi sert l'héritage multiple ? 
        (Réponses à toutes les questions de cette
        page) 
         
         
         
        Classe de base : 
        une classe dérivée peut-elle hériter deux fois d'une
        même classe de base : 
        - directement ? 
        class
        D1 : public B, public B { /* ... */ } ; 
        - indirectement ? 
        class
        A : public L { /* ... */ } ; 
        class B : public L { /* ... */ } ; 
        class D2 : public A, public B { /* ... */ } ; 
        - dans le
        deuxième cas, quel est le graphe acyclique dirigé
        décrivant le mieux la situation ? 
         
        
            
                  | 
                ou bien  | 
                  
                 | 
             
         
        Peut-on caster D2
        en L ? 
        (Réponses à toutes les questions de cette
        page) 
         
         
         
        Héritage virtuel 
        Décrivez par un graphe acyclique
        dirigé la situation suivante : 
        class
        B : { /* ... */ } ; 
        class X : virtual public B { /* ... */ } ; 
        class Y : virtual public B { /* ... */ } ; 
        class Z : public B { /* ... */ } ; 
        class DDD : public X, public Y, public Z { /* ... */ } ; 
         
        Dans ce dernier cas, comment la fonction virtuelle f() de
        B, redéfinie dans X, Y, Z doit elle être implémentée
        dans DDD afin d'appeler sans conflit chacune des versions
        de f() de ses classes mères ? 
        class B 
        { 
        public: 
           virtual void f() ; 
        }; 
        void
        DDD::f() 
        { 
           ... 
           X::f() ; Y::f() ; Z::f() ; // ? suffisant ? 
        } 
        (Réponses à toutes les questions de cette
        page) 
         
         
         
        Ambiguïtés 
        C'est bon ? Pas trop mal à la tête
        ?... Allez, on passe la seconde. 
        class V  
        { 
        public: 
           int f() ; 
           int fff() ; 
           int v ; 
           int vvv ; 
        }; 
        class A 
        { 
        public: 
           int a ; 
           static int static_a ; 
           enum {e} ; 
        }; 
        class B : public A, public virtual V 
        { 
        public: 
           int f() ; 
           int x ; 
           int fff() ; 
        }; 
        class C : public A, public virtual V 
        { 
        private: 
           char * vvv ; 
        }; 
        class D : public B, public C 
        { 
           void g() ; 
        }; 
        class DDD : public B, public virtual V 
        { 
        }; 
        Avant de préciser si la fonction suivante comporte ou
        non une ou plusieurs ambiguités, faite un diagramme
        acyclique de la situation, avec les attributs et les
        fonctions respectives de chaques classes. 
        Surtout, faites-le. 
        Surtout. 
        void
        D:: g() 
        { 
           v++; //
        ambigüe ou non ? 
           a++; //
        ambigüe ou non ? 
           x++; //
        ambigüe ou non ? 
           f() ; //
        ambigüe ou non ? 
           static_a = 1 ; //
        ambigüe ou non ? 
           int i = e ; //
        ambigüe ou non ? 
         
           B* p_b = this ; //
        ambigüe ou non ? 
           A* p_a = this ; //
        ambigüe ou non ? 
           V* p_v = this ; //
        ambigüe ou non ? 
         
           int my_vvv = vvv ; 
        }  
         
        Quelle
        est la fonction f() appelée dans ggg() ? V::f() ou
        B::f() ? 
        void ggg( B* p_b, DDD* p_ddd ) 
        { 
           p_b->f() ; // V::f()
        ou B::f() ? 
           p_ddd->f() ; // V::f()
        ou B::f() ? 
        } 
        (Réponses à toutes les questions de cette
        page) 
         
         
         
         
        Fonctions virtuelles 
        okok, on respire... on inspire... on
        expire... on inspire un grand coup : c'est reparti. 
        Les questions sont en commentaires : 
        class
        V  
        { 
        public : 
           static virtual int sf() ; // possible ? 
           virtual int f() ; 
           virtual int g( V* ) ; 
           virtual V* h() ; 
        }; 
        class A : public V 
        { 
           int f() // f est-elle virtuelle 
           { 
              f() ; // quelle f() est appelé
        ? 
           }; 
           virtual int g( A* ) ; // s'agit-il de la
        même fonction virtuelle que V::g() ? 
           virtual A* h() ; // s'agit-il de la même
        fonction virtuelle que V::h() ? 
        }; 
         
        Considérez les classes A, B et C : 
        class A 
        { 
        public : 
           virtual void f() {
        printf("A::f()") ; } 
           A() {} 
        }; 
        class B : public A 
        { 
        public : 
           B() { f() ; } //
        appelle la fonction virtuelle f() 
           void g() { f() ; } //
        appelle la fonction virtuelle f() 
        }; 
        class C : public B 
        { 
        public : 
           virtual void f() {
        printf("C::f()") ; } 
           C() {} 
        }; 
        Que produit le
        code suivant ? 
        main() 
        { 
           C c ; 
           c.g() ; 
        } 
        Ultime question : fonctions virtuelles avec
        héritage virtuel. soit : 
          
        Toutes les fonctions sont virtuelles. AL et BL ont un
        héritage virtuel vers LL. 
        quelle est la fonction f() invoquée par le code suivant
        ? 
        void ma_fonction(
        ABL* p_abl ) 
        { 
           BL* p_bl = (BL*)p_abl ; 
           p_bl->f() ; //
        LL::f() ou AL::f() ou error ??? 
        } 
        Remarque : la dernière question de la prochaine
        rubrique propose une autre façon de répondre à la
        même question... 
         
        (Réponses à toutes les questions de cette
        page) 
         
         
         
         
        Héritage multiple et représentation
        en mémoire 
        Dernière ligne droite... et là,
        ça devient vraiment coton... 
        Quelle est la représentation mémoire correcte d'un
        objet de type C, sachant que C dérive des classes A et B
        ?  
         
        
            
                  | 
                ou bien  | 
                  | 
             
         
        Considérez que A
        possède une fonction fa(), B une fonction fb() et C un
        fonction fc(). 
        Comment, dans le cadre de la première représentation
        mémoire (celle de gauche), traduiriez-vous : 
        C* p_c = new C() ; 
        p_c->fb() ; ? 
        Indice : 
        Les adresses mémoire de p_c et (B*)p_c sont-elles les
        mêmes ? 
        Oui ? 
        Non ? 
        Si non, et si p_b = (B*)p_c, est-ce que (p_c == p_b) est
        true ou false ? 
        Si non, pourquoi le code suivant marche-t-il quand-même
        ? 
        C*
        p_c = 0 ; 
        B* p_b = 0 ; 
        if( p_c == 0 ) { /* ... */ } 
        p_b = p_c ; 
        if( p_b == 0 ) { /* ... */ } 
        En effet, si le
        fait de caster un C* en B* devait entraîner un décalage
        mémoire, p_b devrait changer puisque p_b vaut un cast
        implicite de p_c en B*... alors ? 
         
        Quelle est la représentation mémoire d'un
        héritage virtuel tel que celui-ci ? 
          
        Ultime question : héritage virtuel avec fonctions
        virtuelles, le tout en mémoire! 
        Reprenons l'héritage virtuel présenté en fin de
        rubrique précédente : 
          
        Représentez les tableaux de pointeurs vers les fonctions
        virtuelles telles que chaque parties de l'objet ABL doit
        les stocker. 
        Ces tableaux sont, pour chacune des fonctions f(), g(),
        h() et k(), composé d'un offset permettant de retomber
        sur le début de la bonne fonction. 
        Remarque : la dernière question
        de la précédente rubrique propose une autre façon de
        trouver un début de réponse à la même question... 
         
        (Réponses à toutes les questions de cette
        page) 
         
         
         
         
         
         |