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

C++ Discussion :

polymorphisme : problème de constructeur et destructeur


Sujet :

C++

  1. #1
    Membre régulier

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    133
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 133
    Points : 113
    Points
    113
    Par défaut polymorphisme : problème de constructeur et destructeur
    Bonjour. Ca fait depuis ce matin que je planche sur un problème ... J'ai cherché sur le net des exemples et je ne parviens pas à trouver mon erreur.
    Je précise que je programme sur Code::Blocks (sous linux)

    J'ai une classe abstraite et j'aimerais pouvoir définir des classes filles qui redéfinissent les méthodes.
    Voici mon code :

    Fichier test.h
    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
    #ifndef __TEST__
    #define __TEST__
     
    typedef unsigned int entier;
     
    class CMere
    {
      public :
        virtual void Methode1()=0;
        virtual entier Methode2();
        virtual entier Methode3();
        virtual ~CMere();
        CMere();
    };
     
    class CFille : virtual public CMere
    {
      public :
        void Methode1();
        entier Methode2();
        entier Methode3();
        ~CFille();
        CFille();
      private :
        entier Var1;
        entier Var2;
    };
     
    #endif
    Fichier test.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    #include "test.h"
     
    CMere::CMere()
    {}
     
    CMere::~CMere()
    {}
     
    CFille::CFille()
    {}
     
    CFille::~CFille()
    {}
    Mes erreurs
    In function `CMere':
    undefined reference to `vtable for CMere'
    undefined reference to `vtable for CMere'
    In function `~CMere':
    undefined reference to `vtable for CMere'
    undefined reference to `vtable for CMere'
    undefined reference to `vtable for CMere'
    undefined reference to `typeinfo for CMere'
    === Build finished: 6 errors, 0 warnings ===
    Si vous pouviez m'expliquer la raison de ces erreurs ...

    Merci d'avance.

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 625
    Points : 30 671
    Points
    30 671
    Par défaut
    Salut,

    Perso, j'ai pris l'habitude de déclarer les destructeurs des classes dérivées en tant que virtuels, et bien sur, de les définir (même s'il sont vides)

    N'oublie pas non plus de définir les méthodes virtuelles non pures de ta classe de base (ou de les déclarer comme virtuelles pures si tu n'a pas ce qui est nécessaire pour leur implémentation)

    Je crois que ces deux conseils devraient t'aider à résoudre ton problème de référence indéfinie
    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 régulier

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    133
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 133
    Points : 113
    Points
    113
    Par défaut
    Citation Envoyé par koala01 Voir le message
    Perso, j'ai pris l'habitude de déclarer les destructeurs des classes dérivées en tant que virtuels, et bien sur, de les définir (même s'il sont vides)
    ça ne change strictement rien

    Citation Envoyé par koala01 Voir le message
    N'oublie pas non plus de définir les méthodes virtuelles non pures de ta classe de base (ou de les déclarer comme virtuelles pures si tu n'a pas ce qui est nécessaire pour leur implémentation)
    je n'ai que des méthodes virtuelles, sauf le constructeur

    la classe CMere ne sera jamais instanciée vu que abstraite, donc je me dis que le constructeur ne sert à rien mais si je l'enlève ça ne marche toujours pas.

    le =0 que je met dans la classe CMere pour en faire une classe abstraite, je dois le mettre à un seul endroit ? nimporte lequel ? ou partout ?

  4. #4
    Membre averti
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2006
    Messages : 366
    Points : 444
    Points
    444
    Par défaut
    Citation Envoyé par Michel_57 Voir le message
    ça ne change strictement rien



    je n'ai que des méthodes virtuelles, sauf le constructeur

    la classe CMere ne sera jamais instanciée vu que abstraite, donc je me dis que le constructeur ne sert à rien mais si je l'enlève ça ne marche toujours pas.
    Même si la classe n'est pas instanciable, elle peut avoir des membres qu'il faut initialiser. Le constructeur est là pour ça, et tu peux le mettre en protected vu qu'il ne sera appelé que par les constructeurs des classes filles.

    le =0 que je met dans la classe CMere pour en faire une classe abstraite, je dois le mettre à un seul endroit ? nimporte lequel ? ou partout ?
    Le =0 placé après une méthode signifie que cette méthode est virtuelle pure, elle ne sera pas définie dans cette classe. Donc il faut le mettre pour chacune des fonctions que tu veux rendre comme tel.

    La classe sera abstraite si elle contient au moins une fonction virtuelle pure.

    Pour ton problème, enlève le mot clé virtual dans la déclaration d'héritage :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    class CFille : public CMere

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 625
    Points : 30 671
    Points
    30 671
    Par défaut
    En fait, le "=0" indique au compilateur (et à l'éditeur de liens) quelque chose comme
    Cette fonction existe (dans les classes dérivées) mais tu n'en trouvera pas l'implémentation ici
    avec comme sous entendu que tu ne dispose pas des membres qui permettent de fournir le comportement de cette méthode.

    Dés lors, toute fonction que tu veux mettre dans ton interface (AKA dans la classe de base) mais dont tu ne sait pas fournir le comportement doit être
    1. Déclarée comme fonction virtuelle pure dans l'interface
    2. Redéclarée et définie dans les classes concrète dérivées (évidemment, s'il s'avère qu'une classe dérivée doit - elle aussi - être abstraite, il ne sert à rien de redéclarer les fonctions pour lesquelles tu ne dispose pas des informations nécessaires )


    [EDIT]Grilled
    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

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 625
    Points : 30 671
    Points
    30 671
    Par défaut
    Ah, oui, de fait, je n'avais pas remarqué l'héritage virtuel...

    En fait, tous tes problèmes viennent sans doute de là:

    Quand tu déclare un héritage virtuel, il faut appeler explicitement le constructeur de la classe "mere" dans le constructeur de la classe "fille":
    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
     
    class Mere
    {
        public:
            Mere();
           /*...*/
    };
    class Fille:virtual public Mere
    {
        public:
            Fille();
            /* ...*/
    };
    class PetiteFille:public Fille
    {
        public:
            PetiteFille();
            /*...*/
    };
    devra être implémenté sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Fille::Fille():mere()/*,liste d'initialisation */
    {
        /*...*/
    }
    PetiteFille::PetiteFille():Fille()/*, liste d'initialisation */
    {
        /*...*/
    }
    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

  7. #7
    Membre régulier

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    133
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 133
    Points : 113
    Points
    113
    Par défaut
    ha ok tout s'éclaircit !
    il n'y a plus d'erreur

    c'est déjà la 2ème fois que tu m'aides koala
    si je peux t'aider aussi n'hésites pas à me contacter ! non je déconne, comme si je pouvais t'aider
    si tu passes par metz un de ces jours dis-moi, je t'invite à boire une petite mousse


  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 625
    Points : 30 671
    Points
    30 671
    Par défaut
    Citation Envoyé par Michel_57 Voir le message
    <snip>
    si tu passes par metz un de ces jours dis-moi, je t'invite à boire une petite mousse

    Je n'ai pas vraiment les moyens de voyager pour l'instant, mais, ne t'en fait pas: si un jour je passe du coté de metz, je saurai te rappeler l'offre

    Ceci dit, et pour être tout à fait complet, l'héritage virtuel n'est à envisager que dans le cas de ce que l'on appelle communément l'héritage "en diamant".

    En effet, si tu as un arbre d'héritage qui ressemble à
    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
    class Mere
    {
        /*...*/
    };
    class Fille1 :public Mere
    {
        /*...*/
    };
    class Fille2: public Mere
    {
        /*...*/
    };
    class PetiteFille:public Fille2, public Fille1
    {
        /*...*/
    };
    tu te trouvera confronté au problème que, si tu veux appeler une méthode qui se trouve dans Mere alors que tu dispose d'un objet de type PetiteFille, il y aura conflit entre la méthode qui est héritée de Fille1 et celle qui est héritée de Fille2.

    Du coup; tu peux envisager de préciser quelle méthode tu appelle sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Fille1::LaMethode(/*les parametres*/)
    /*ou*/
    Fille2::LaMethode(/*les parametres*/)
    mais cela reste malgré tout fort lourd, et, en plus, tout à fait inutile si LaMethode fait exactement dans Fille1 que dans Fille2...

    Il faut d'ailleurs noter que ce qui est vrai pour les méthodes l'est aussi pour les membre: tu aura réellement deux fois chaque membre de la classe Mere dans la classe PetiteFille... Ce qui n'est bien souvent pas ce que tu souhaite

    Dés lors, il faut signaler au compilateur qu'il ne faut q'une seule instance de la classe Mere, et, pour cela, on signale que l'héritage reçu par Fille1 et par Fille2 de Mere est... virtuel.

    Evidemment, on peut encore longuement discuter de l'opportunité d'avoir un héritage multiple - et à plus forte raison de l'héritage "en diamant"- mais, dans certains cas, cette possibilité donnée par le langage peut malgré tout s'avérer utile
    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

  9. #9
    Membre régulier

    Profil pro
    Inscrit en
    Décembre 2003
    Messages
    133
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2003
    Messages : 133
    Points : 113
    Points
    113
    Par défaut
    je n'ai pas d'héritage en diamant dans mon cas mais la classe abstraite est quand même indispensable (je crois).

    car en fait j'ai une fonction de traitement dans une classe auxiliaire qui prend en paramètre une instance de CMere. et j'utilise cette technique pour que ma classe auxiliaire puisse traiter tout ce qui dérive de CMere, mais pas CMere elle-même.
    CMere sert juste à indiquer quelles sont les méthodes obligatoires à coder pour que le traitement dans la classe auxiliaire puisse se faire, et ce, sur des classes très différentes.

    pour reprendre un exemple très parlant que j'ai vu sur le net, c'est comme si j'avais une classe Animal abstraite. je peux l'utiliser pour en faire autant de dérivées que je veux, avec l'obligation de définir la méthode Cri() qui est en virtual dans Animal.
    ainsi je peux faire un tableau de Animal* qui contient des instances de Chat, Chien, Loup, ...
    et je peux faire "crier" tous les animaux de mon tableau avec une seule boucle malgré que ce soient des classes différentes. je suis ainsi sûr que la méthode Cri() est présente et définie. puisque dans Animal, la méthode Cri() ne peut pas être définie, ça n'a pas de sens.

    enfin j'aurai tout le loisir d'essayer les héritages en diamant plus tard ^^
    je garde ton post au chaud quelque part ^^

  10. #10
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 625
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 625
    Points : 30 671
    Points
    30 671
    Par défaut
    Ne t'en fait pas, je sais tres bien ce qu'est le polymorphisme...

    Je n'apportait une précision que sur ce à quoi sert l'héritage virtuel, qui n'a rien à voir avec le polymorphisme, en expliquant les problèmes qu'il permet de résoudre
    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. Problème de constructeur de copie ?
    Par Bestiol dans le forum C++
    Réponses: 6
    Dernier message: 03/11/2006, 11h28
  2. Réponses: 4
    Dernier message: 21/09/2006, 12h45
  3. [POO]Probléme de constructeur virtuel surchargé
    Par Laurent Dardenne dans le forum Delphi
    Réponses: 10
    Dernier message: 15/08/2006, 12h19
  4. [C#] DLL, problème de constructeur
    Par gmonta dans le forum C#
    Réponses: 4
    Dernier message: 30/11/2005, 09h43
  5. Réponses: 24
    Dernier message: 10/06/2005, 10h11

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