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 :

[POO] Implémentation de fonction virtuelle pure dans la classe de base


Sujet :

C++

  1. #1
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut [POO] Implémentation de fonction virtuelle pure dans la classe de base
    Bonjour,

    Dans le code suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class CTest
    {
    public:
    	CTest();
    	~CTest();
    	virtual void MaFonction(void) = 0;
    };
     
    CTest::CTest() { }
    CTest::~CTest() { }
    void CTest::MaFonction(void) { }
    Je déclare la classe CTest avec une fonction virtuelle pure (la fonction MaFonction) mais le compilateur me laisse créer une implémentation de cette fonction. Par contre, lorsque j'essaye d'instancier cette classe de base, le compilateur génère une erreur normale "error C2259: 'CTest'*: impossible d'instancier une classe abstraite"

    Quel sens cela peut-il avoir de déclarer dans la classe de base un fonction virtuelle pure ET d'avoir une implémentation de cette fonction aussi dans la classe de base.

    Comment se comportent les autres compilateurs (je suis sur Visual Studio 2005 C++ en niveau de warning le plus élevé)

    Merci de vos réponses
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  2. #2
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    En principe, les autres se comporteront pareil je crois.

    Par la création d'une fonction virtuelle _pure_, tu empêches la classe d'être instanciée.
    En donnant une implémentation toutefois, tu fournis un comportement par défaut pour cette fonction utilisable dans les classes filles.

    Il me semble que ça se passe comme ça.

  3. #3
    Membre éclairé
    Avatar de Florian Goo
    Profil pro
    Inscrit en
    Septembre 2008
    Messages
    680
    Détails du profil
    Informations personnelles :
    Âge : 38
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Septembre 2008
    Messages : 680
    Points : 858
    Points
    858
    Par défaut
    Bonjour,

    Comportement identique avec GCC 4.4(svn), niveau de warning maximum.

    Que je déclare (sans la définir) CTestFille::MaFonction() ou non, c'est soit undefined reference, soit impossible d'instancier car fonction virtuelle pure.
    Par contre, si je fais ceci :
    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
    #include <iostream>
     
    class CTest
    {
    public:
    	CTest();
    	~CTest();
    	virtual void MaFonction(void) = 0;
    };
     
    CTest::CTest() { }
    CTest::~CTest() { }
    void CTest::MaFonction(void)
    {
    	std::cout << "yeah" << std::endl;
    }
     
     
     
    struct CTest2: public CTest
    {
    	void MaFonction(void);
    };
     
    void CTest2::MaFonction(void)
    {
    	CTest::MaFonction();
    }
     
     
    int main()
    {
    	CTest2 t;
    	t.MaFonction();
     
    	return 0;
    }
    On a bien la sortie attendue
    Cours : Initiation à CMake
    Projet : Scalpel, bibliothèque d'analyse de code source C++ (développement en cours)
    Ce message a été tapé avec un clavier en disposition bépo.

  4. #4
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Alp Voir le message
    En principe, les autres se comporteront pareil je crois.

    Par la création d'une fonction virtuelle _pure_, tu empêches la classe d'être instanciée.
    En donnant une implémentation toutefois, tu fournis un comportement par défaut pour cette fonction utilisable dans les classes filles.

    Il me semble que ça se passe comme ça.
    C'est ça: ce sont deux notions séparées. Virtuelle pure implique que non instanciable et forcément redéfinie par une classe fille instanciable.
    Par contre, rien n'empêche de fournir un comportement. Plutôt qu'un comportement par défaut qui laisserait entendre que la classe fille ne se contente que d'appeler la méthode parente, je dirais un comportement de base factorisé. Mais j'avoue que je ne vois pas trop d'exemple où cela se justifie et qui rendrait une autre solution moins appropriée.

  5. #5
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    C'est le genre de trucs qui peut servir lorsque l'on est amené à passer des tests lors d'un entretien... En pratique, je n'ai jamais vu un cas ou c'était la seule solution. Ca existe peut-être qui sait.

  6. #6
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Alp, il y a presque toujours l'alternative de fournir un membre ayant le comportement qui aurait ete donne a l'implementation de la classe virtuelle pure. Mais il y a un cas ou c'est impossible: quand on veut rendre le destructeur virtuel pur. Ce cas est rare -- ca m'est arrive une fois me semble-t'il: la decision de la maniere dont il fallait disposer d'une ressource etait deleguee aux descendants de la classe de base. On ne pouvait pas utiliser une fonction virtuelle pure autre que le destructeur -- durant l'execution d'un destructeur le type dynamique est le type statique.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  7. #7
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par Jean-Marc.Bourguet Voir le message
    Mais il y a un cas ou c'est impossible: quand on veut rendre le destructeur virtuel pur.
    ++

  8. #8
    Rédacteur

    Avatar de ram-0000
    Homme Profil pro
    Consultant en sécurité
    Inscrit en
    Mai 2007
    Messages
    11 517
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Consultant en sécurité
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mai 2007
    Messages : 11 517
    Points : 50 367
    Points
    50 367
    Par défaut
    Citation Envoyé par 3DArchi Voir le message
    C'est ça: ce sont deux notions séparées. Virtuelle pure implique que non instanciable et forcément redéfinie par une classe fille instanciable.
    Par contre, rien n'empêche de fournir un comportement. Plutôt qu'un comportement par défaut qui laisserait entendre que la classe fille ne se contente que d'appeler la méthode parente, je dirais un comportement de base factorisé. Mais j'avoue que je ne vois pas trop d'exemple où cela se justifie et qui rendrait une autre solution moins appropriée.

    Donc cela veut dire que la classe fille à la droit (et même doit) d'appeler la classe mère pour la gestion de certains "trucs" factorisés par la classe mère. Il faut alors bien indiquer dans la documentation classe mere que la fonction DOIT appeler la fonction de 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
    20
    21
    22
    23
    24
    25
     
    class CMere
    {
       public:
       virtual void MaFonction(void) = 0;  // fonction virtuelle pure mais définie. Cette fonction doit impérativement être appelée par la fonction MaFonction() de toute classe mère 
    };
    void CMere::MaFonction(void)
    {
       // code de la classe CMere
       ...
    }
     
    class CFille : public CMere
    {
       public:
       virtual void MaFonction(void);
    };
     
    void CFille::MaFonction(void)
    {
       // code de la classe CFille
       ...
       CMere::MaFonction();
       ...
    }
    Raymond
    Vous souhaitez participer à la rubrique Réseaux ? Contactez-moi

    Cafuro Cafuro est un outil SNMP dont le but est d'aider les administrateurs système et réseau à configurer leurs équipements SNMP réseau.
    e-verbe Un logiciel de conjugaison des verbes de la langue française.

    Ma page personnelle sur DVP
    .

  9. #9
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Citation Envoyé par ram-0000 Voir le message
    Donc cela veut dire que la classe fille à la droit (et même doit) d'appeler la classe mère pour la gestion de certains "trucs" factorisés par la classe mère. Il faut alors bien indiquer dans la documentation classe mere que la fonction DOIT appeler la fonction de classe fille.
    C'est bien pour ça (en dehors du cas brillamment cité par Jean-Marc) lorsque tu fait ça, c'est que tu as un problème de conception.

    Tu peux souvent remplacer ta fonction virtuelle (MaMethodeVirtuelle) pure par quelque chose comme ça:
    MaMethode (non virtuelle):
    1/Pre-condition (non virtuelle)
    2/MaMethodeVirtuellePureSansCode
    3/Post-condition (non virtuelle)

  10. #10
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    au boulot on a un cas d'utilisation ou c'est pratique pour verifier qu'une hierarchie d'appel est bien effectué.

    de memoire, c'est un truc du genre

    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
     
    class BaseClass
    {
        bool BaseClassFonctionAppelee;
    private:
        virtual void FonctionAppeleLorsDunChargement() = 0
        { 
            BaseClassFonctionAppelee = true;
        }
    public:
        void chargement()
        {
            BaseClassFonctionAppelee  = false;
            FonctionAppeleLorsDunChargement();
            Assert(BaseClassFonctionAppelee );
        }
    }
     
    class SousClass : public BaseClass
    {
    private:
       virtual void FonctionAppeleLorsDunChargement() 
        { 
            BaseClass::FonctionAppeleLorsDunChargement();
            // initialise des donnée
        }
    }
    c'est pour une structure d'objets dynamiques qui a besoin d'être initialisé de la même façon qu'avec des constructeurs.
    Avec ça, on peut vérifier que les sous classe appellent bien le "constructeur" de la classe parente, et en même temps, on la force à redéfinir ce constructeur...

    bon, ce n'est pas le cas d'utilisation ultime de cette feature du C++, on aurait probablement pu l'implémenter sans, mais c'est bien pratique quand même
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  11. #11
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    class SousSousClass : public SousClass
    {
    private:
       virtual void FonctionAppeleLorsDunChargement() 
        { 
            BaseClass::FonctionAppeleLorsDunChargement();
            // initialise des donnée
           // Mais n'a pas appele SousClass::FonctionAppeleLorsDunChargement()
        }
    }

  12. #12
    Rédacteur
    Avatar de bafman
    Profil pro
    Développeur informatique
    Inscrit en
    Novembre 2003
    Messages
    2 574
    Détails du profil
    Informations personnelles :
    Âge : 40
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Novembre 2003
    Messages : 2 574
    Points : 5 323
    Points
    5 323
    Par défaut
    en fait, on fait plutôt ça

    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
     
    class SousClass : public BaseClass
    {
        typedef BaseClass parent_type;
        virtual void VirtualFunc()
        {
            parent_type::VirtualFunc();
        }
    }
     
    class SousSousClass : public SousClass
    {
        typedef SousClass parent_type;
        virtual void VirtualFunc()
        {
            parent_type::VirtualFunc();
        }
    }
    mais bien entendu, tout ceci est géré automatiquement via des macro et autre qui nous permettent de définir le type RTTI dynamique de l'objet...

    bref, ceci n'est pas le propos de ce post, c'était juste un exemple d'utilisation de l'implémentation d'une fonction virtuelle pure
    * Il est infiniment plus simple de faire rapidement un code qui marche que de faire un code rapide qui marche
    * pour faciliter les recherches, n'oubliez pas de voter pour les réponses pertinentes
    Mes articles

  13. #13
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    En fait, ce que je voulais dire, c'est que rien n'empêche de trapper un étage en remontant les appels. Je pense que quelque chose comme ça
    Citation Envoyé par moi-même
    1/Pre-condition (non virtuelle)
    2/MaMethodeVirtuellePureSansCode
    3/Post-condition (non virtuelle)
    c'est moins bancal. T'es assuré de toujours remonter à ta classe de base.

  14. #14
    Membre du Club
    Inscrit en
    Mai 2005
    Messages
    73
    Détails du profil
    Informations forums :
    Inscription : Mai 2005
    Messages : 73
    Points : 68
    Points
    68
    Par défaut
    Dans le GotW #31, Herb Sutter évoque ce sujet. Pour résumer, il distingue trois cas où cela peut être utile :
    - Destructeur virtuel pur : la classe doit être abstraite, mais aucune de ces méthodes ne peut être virtuelle pure (ça n'aurait pas de sens) --> on met le destructeur en virtuel pur, mais il doit tout de même être implémenté.

    - Forcer l'explicitation de l'utilisation d'un comportement par défaut : on implémente une méthode virtuelle, afin de représenter un comportement par défaut, mais on oblige les classes dérivées à indiquer explicitement qu'elles utilisent ce comportement.
    Exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct Base
    {
        virtual void foo() const = 0;
    };
     
    void Base::foo() const { std::cout << "Comportement par défaut"; }
     
    struct Derivee : public Base
    {
        virtual void foo() const { Base::foo(); } //utilise le comportement par défaut
    };
    - Dernier cas, plus anecdotique : obtenir des messages d'erreurs plus significatifs lorsqu'on invoque par "accident" une méthode virtuelle pure

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

Discussions similaires

  1. Réponses: 4
    Dernier message: 11/10/2013, 12h08
  2. Réponses: 4
    Dernier message: 09/10/2008, 09h04
  3. Réponses: 2
    Dernier message: 02/10/2008, 16h37
  4. Réponses: 1
    Dernier message: 02/05/2008, 20h34
  5. Compilation avec des fonctions virtuel pure
    Par vanitom dans le forum C++
    Réponses: 4
    Dernier message: 16/12/2005, 14h37

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