IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

[libraries] problemes avec le polymorphisme


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut [libraries] problemes avec le polymorphisme
    Bonjour

    je rencontre un problème étonnant ...

    voici le principe que j'ai, avec 3 logiciels :
    • une librairie static qui contient
      • un interface IThread avec deux méthodes virtuelles pures
        • start
        • stop
      • Thread qui dérive de IThread et implémente les deux méthodes (start et stop)
    • une librairie shared qui contient un objet Alpha qui dérive Thread
    • un logiciel exécutable Main qui charge avec dlopen la librairie shared et instancie l'objet Alpha sous sa forme IThread

    (il va de soit que toutes ces librairies sont référencées et linkées correctement pour que cela puisse se compiler et s’exécuter sans problème)

    si j'ai redéfini les méthodes start et stop dans l'objet Alpha comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    virtual bool start() {
    		printf("[libYYYY::Alpha] : start \n");
    		return Thread::start();
    	}
    	virtual bool stop() {
    		printf("[libYYYY::Alpha] : stop \n");
    		return true;
    	}
    et que dans Main j'appel le start() sur IThread tout marche bien

    si par contre je commente la fonction start() et que je ne change pas le code d'appel de Main
    ça appel le stop() ... et pas le start() du parent c'est a dire celui du Thread, comme s'il ne pouvait résoudre le polymorphisme ...

    si vous aviez quelques pistes ça intéresserait beaucoup ....

    Merci

  2. #2
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    J'aurais tendance à dire que tu n'as simplement pas fait afficher quoi que ce soit dans la fonction start de IThread...

    Si tu as une fonction virtuelle -- pour laquelle tu donne une implémentation -- dans la classe de base et que tu ne redéfinis pas le comportement de cette fonction dans la classe dérivée, il est tout à fait normal que ce soit le comportement défini pour la classe de base qui soit utilisé, même si tu appelles cette fonction au départ d'un (pointeur / (e) référence) connu comme étant du type de base (IThread dans le cas présent) mais faisant référence à un objet de type dérivé (en l'occurrence Alpha) .

    S'il n'y a pas d'affichage prévu dans l'implémentation de la fonction au niveau de la classe de base, tu ne verras tout simplement pas que c'est ce comportement là qui est réellement invoqué et, si tu provoque un affichage dans l'implémentation spécifique à un type dérivé d'une fonction qui sera appelé juste après (ou peu s'en faut), tu peux avoir l'impression que seule "la fonction appelée après" est appelée

    Mais, de manière générale, il n'est pas bon de provoquer une sortie (avec printf std::cout ou std::cerr) pour le débuggage dans les fonctions qui ne sont pas sensées afficher quoi que ce soit : si tu veux savoir ce qui est réellement effectué, tu dois utiliser le débuggeur
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    pour info dans toutes les fonctions j'ai collé un cout ...
    ceci étant ça explique pas vraiment pourquoi quand je lance le start il passe dans le stop !!! (si le start n'est pas défini explicitement dans Alpha) et je me doute bien qu'avec le polymorphisme il devrait normalement aller dans le start de Thread ....

    et effectivement avec le debugger quand je fait alpha->start(); .... ca va dans le stop() lol

    maintenant j'ai même pire dans un autre bout de code : si je met le start avant le stop
    comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    virtual bool start() {
    		Log::i(LCN, "start");
    		cout << "[" << LCN << "] " << "start" << endl;
    		Thread::start();
    		return true;
    	}
     
    	virtual bool stop() {
    		Log::i(LCN, "stop");
    		cout << "[" << LCN << "] " << "stop" << endl;
    		Thread::stop();
    		return true;
    	}
    il passe dans le stop
    pour le faire passer dans le start il faut que je fasse
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    	virtual bool stop() {
    		Log::i(LCN, "stop");
    		cout << "[" << LCN << "] " << "stop" << endl;
    		Thread::stop();
    		return true;
    	}
     
    	virtual bool start() {
    		Log::i(LCN, "start");
    		cout << "[" << LCN << "] " << "start" << endl;
    		Thread::start();
    		return true;
    	}
    je me demande si le compilateur serait pas carrement dans les choux !!!

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Plutôt que de ne nous donner que des portions de code, pourrais tu s'il te plait nous donner le contenu intégral
    • du fichier d'en-tête dans lequel tu as défini la classe de base (thread.h/.hpp?)
    • du fichier d'implémentation des fonctions de la classe de base (thread.cpp?)
    • du fichier d'en-tête dans lequel tu as défini la classe Alpha Alpha.h/.hpp?)
    • du fichier d'implémentation des fonctions de la classe Alpha (Alpha.cpp?)


    et nous indiquer un code minimal compilable qui puisse reproduire ton problème

    A mon avis, si tu ne profites pas du polymorphisme, c'est très vraisemblablement parce que tu transmet un objet de type dérivé (Alpha) à une fonction qui attend un objet de type IThread qui devrait lui être transmis par valeur. Or, pour profiter du polymorphisme, il faut passer par l'utilisation des pointeurs ou, plus sécurisant du fait de la garantie de non nullité (ou, si tu préfères, de la garantie d'existence de la variable référencée) par référence.

    En effet, lorsqu'un paramètre de fonction est passé par valeur au lieu d'être passé par référence (éventuellement constante) ou par pointeur, il y a copie de l'élément transmis, mais il souffre du phénomène de slicing qui fait que seule la partie relative au type de base est copiée (et, du coup, c'est effectivement un objet du type de base et non un objet du type dérivé que la fonction reçoit).

    Pour éviter cela, il faut veiller à respecter la sémantique d'entité des classes intervenant dans une hiérarchie de classes en veillant à les rendre non copiables et non affectables (différentes possibilités sont offertes, en fonction de la norme C++ utilisée pour le projet ), ce qui aura pour effet de provoquer une erreur de compilation (ou, dans le pire des pire des cas, d'édition des liens) si tu essayes de faire appel à une fonction qui attend un objet du type de base transmis par valeur
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre Expert
    Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 562
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 562
    Par défaut
    en fait j'ai trouvé d'ou cela venait
    je faisais des cast (IThread *) et il fallait faire des dynamic_cast<IThread *>()
    c'est pour cela que je perdais la table des fonctions virtuelles

    ceci étant c++ aurait pu évoluer pour éviter ce genre de prob !!!! d'autant que je ne vois pas l'interet de transformer un object X en sont parent juste pour appeler une methode en directe de celui et encore faut pas qu'elle soit virtuelle

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 152
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 152
    Billets dans le blog
    4
    Par défaut
    Si tu es sûr que le cast est possible, un static_cast suffit.
    Le cast C-style est un équivalent d'un reinterpret_cast. Et c'est généralement pas le genre de cast qu'on emploie au quotidien.
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  7. #7
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par ikeas Voir le message
    en fait j'ai trouvé d'ou cela venait
    je faisais des cast (IThread *) et il fallait faire des dynamic_cast<IThread *>()
    c'est pour cela que je perdais la table des fonctions virtuelles

    ceci étant c++ aurait pu évoluer pour éviter ce genre de prob !!!! d'autant que je ne vois pas l'interet de transformer un object X en sont parent juste pour appeler une methode en directe de celui et encore faut pas qu'elle soit virtuelle
    C'est surtout que tu n'as absolument pas besoin de faire le moindre cast d'un objet de type dérivé pour le transmettre à une fonction qui attend un paramètre du type de base!!!

    Un petit code d'exemple pour t'en convaincre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    // dans des fichiers .h / .hpp séparés, le plus souvent
    class Base{
        public:
            virtual ~Base();
            virtual void foo() const;
    };
    class Derivee1 : public Base{
        public:
             /* override est spécifique à C++11, tu peux le retirer si tu utilises
              * une norme antérieure
              */
            void foo() const override;
    };
    class Derivee2: public Base{
        public:
             /* override est spécifique à C++11, tu peux le retirer si tu utilises
              * une norme antérieure
              */
            void foo() const override;
    };
    //dans des fichier .cpp séparés le plus souvent
    #include <iostream>
    Base::~Base(){
    }
    void Base::foo() const{
        std::cout<<"Appel de Base::foo\n";
    }
     
    void Derivee1::foo() const{
        std::cout<<"Appel de Derivee1::foo\n";
    }
     
    void Derivee2::foo() const{
        std::cout<<"Appel de Derivee2::foo\n"
                      <<"avec appel de Base::foo :";
        Base::foo();
    }
    /* une fonction qui prend une référence constante sur le type de base
     */
    void bar(Base const & b){
        b.foo(); /* appellera Base::foo si le type réel de la variable transmise pour b
                  * est Base,  Derivee1::foo si le type réel de la variable transmise pour b
                  * est Derivee1 et Derivee2::foo si c'est Derivee2
                  */
    }
    int main(){
        Base b;
        bar(b); // affiche Appel de Base::foo
        Derivee1 d1;
        bar(d1); // affiche "appel de Derivee1::foo"
        Derivee2 d2;
        bar(d2); /* affiche ""Appel de Derivee2::foo
                  * avec appel de Base::foo : Appel de Base::foo
                  */
        return 0;
    }
    Mais, comme je présume que tu seras plutôt dans une situation dans laquelle tu auras un tableau d'objet polymorphes connus comme étant de type Base, on peut modifier la fonction main pour qu'elle prenne la forme (en C++11) de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    #include <vector>
    #include <memory>
    int main(){
        std::vector<std::unique_ptr<Base>> tab;
        tab.emplace_back(new Base);
        tab.emplace_back(new Derivee1);
        tab.emplace_back(new Derivee2);
        for(auto const & it : tab){
            bar(*(it.get()));
        }
        return 0;
    }
    Et, si tu ne disposes pas de C++11, cela pourrait prendre une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     
    #include <vector>
    int main(){
        std::vector<Base *> tab;
        tab.push_back(new Base);
        tab.push_back(new Derivee1);
        tab.push_back(new Derivee2);
        for(size_t i = 0;i<tab.size();++i){
            bar(*(tab[i]));
        }
        /* n'oublions pas de libérer la mémoire allouée aux pointeurs
         */
        for(size_t i = 0;i<tab.size();++i){
            delete tab[i];
        }
        return 0;
    }
    Ces trois versions de la fonction main provoqueront exactement la même sortie qui ressemblera à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    Appel de Base::foo
    appel de Derivee1::foo
    Appel de Derivee2::foo
    avec appel de Base::foo : Appel de Base::foo
    Magique, non
    [EDIT]La seule chose que tu aies à faire, c'est de maintenir la variable connue comme étant du type de base sous la forme d'un pointeur (de préférence intelligent) pour lequel tu auras eu recours à l'allocation dynamique de la mémoire ou d'une référence (éventuellement constante) sur une instance du type dérivé dont la durée de vie dépasse celle de la référence. Voir, c'est toujours mieux, sous la forme d'une instance du type dérivé, si tu connais le type réel utilisé à la compilation (et que tu ne dois donc pas choisir le type de la variable à l'exécution )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Probleme avec le polymorphisme (si c'en est vraiment)
    Par Porkipic dans le forum Débuter
    Réponses: 7
    Dernier message: 16/12/2014, 01h06
  2. Réponses: 0
    Dernier message: 03/05/2010, 17h07
  3. problem avec programme polymorphisme
    Par domxaline dans le forum Débuter avec Java
    Réponses: 15
    Dernier message: 05/02/2010, 15h25
  4. Réponses: 14
    Dernier message: 09/05/2006, 15h23
  5. Problème avec la library Allegro
    Par Mat 74 dans le forum Allegro
    Réponses: 2
    Dernier message: 09/04/2006, 15h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo