2 votes

Hérite de deux classes polymorphes

Étant donné le code suivant

class T {
    public:
        virtual ~T () {}
        virtual void foo () = 0;
};

class U {
    public:
        U() {}
        ~U() {}
        void bar () { std::cout << "bar" << std::endl; }
};

class A : public U, public T {
    public:
        void foo () { std::cout << "foo" << std::endl; }
};

int main () {
    A * a = new A;

    std::vector<U*> u;
    std::vector<T*> t;

    u.push_back(a);

    t.push_back(reinterpret_cast<T*>(u[0]));

    u[0]->bar ();
    t[0]->foo ();

    delete a;
    return 0;
}

J'obtiens le résultat escompté

bar
foo

Toutefois, si je modifie la définition de U a

class U {
    public:
        U() {}
        virtual ~U() {}
        virtual void bar () { std::cout << "bar" << std::endl; }
};

Je compile toujours correctement et sans avertissements/erreurs, mais la sortie est maintenant la suivante

bar
bar

Qu'est-ce qui, dans la déclaration virtuelle, m'empêche de faire appel à la fonction foo ?

4voto

AndreyT Points 139512

Premièrement, il n'y a pas de classes de base virtuelles dans votre exemple. Les classes qui contiennent des fonctions virtuelles sont appelées polymorphe . (Il existe des "classes de base virtuelles" en C++, mais cela n'a rien à voir avec votre exemple).

Deuxièmement, le comportement de votre code ne dépend d'aucune déclaration virtuelle. Vous avez délibérément détruit l'intégrité du pointeur de base en utilisant reinterpret_cast . Pour cette raison, le comportement du code est indéfini .

Une conversion directe d'un pointeur de base en un autre (ce que vous essayez de faire dans votre code) est appelée coulée croisée . La seule fonte en C++ qui peut effectuer une fonte croisée est la fonte dynamic_cast .

t.push_back(dynamic_cast<T *>(u[0])); 

Vous pouvez effectuer un moulage croisé indirect sans dynamic_cast mais pour cela, il faut d'abord downcaster le pointeur vers le type dérivé ( A * ) en utilisant static_cast puis le convertir en un autre type de pointeur de base

t.push_back(static_cast<A *>(u[0])); // upconversion to `T *` is implicit

1voto

Kornel Kisielewicz Points 26556

Si vous utilisez reinterpret_cast vous perdez toutes les garanties et tout ce que vous faites est un "comportement non défini". Dans ce cas, je suppose que le VMT a été perturbé ou que le VPTR a été écrasé.

A titre d'illustration, lorsque je compile le premier code ci-dessus, j'obtiens un segfault à l'exécution sur mon compilateur.

Si l'on veut vraiment procéder à une "exécution croisée", il faut dériver d'une classe de base commune, et hériter de cette classe de base par U et T. virtuellement ( : virtual public ), ou utiliser dynamic_cast au lieu de reinterpret_cast .

0voto

Jon-Eric Points 7749

Peupler t comme vous l'avez fait u :

t.push_back(a);

Vous n'avez pas besoin reinterpret_cast parce que A est un T .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X