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 :

Pattern NVI : préconisé dans tous les cas ?


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut Pattern NVI : préconisé dans tous les cas ?
    Bonjour,
    La question est dans le titre.
    Mais je suppose que la raison pour laquelle je (me) pose cette question permettra d'affiner les réponses...

    Je pense que j'ai compris l'intérêt du pattern « Non Virtual Interface », mais il y a quelques cas pour lesquels je me demande s'il est vraiment utile/nécessaire.
    À chaque fois, il n'y a ni précondition, ni postcondition.


    Un bout de code est souvent plus parlant qu'un peu de blabla...
    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    class base
    {
     
      public:
        virtual
        ~base()
        {}
     
        virtual
        bool is_cat1() const
        { return false; }
     
        virtual
        bool is_cat2() const
        { return false; }
     
        (...)
     
        virtual
        type1 get_value1() const
        { throw std::runtime_error("Invalid operation."); }
     
        virtual
        type2 get_value2() const
        { throw std::runtime_error("Invalid operation."); }
     
        (...)
     
    }; // class base
     
     
    class deriv1 : public base
    {
     
      private:
        type1 m_value;
     
      public:
        virtual
        bool is_cat1() const
        { return true; }
     
        virtual
        type1 get_value1() const
        { return m_value; }
     
    }; // class deriv1
     
     
    class deriv2 : public base
    {
     
      private:
        type2 m_value;
     
      public:
        virtual
        bool is_cat1() const
        { return true; }
     
        virtual
        type2 get_value2() const
        { return m_value; }
     
    }; // class deriv2
     
    (...)
    Bon, je suppose que ce bout de code est suffisamment parlant que pour que voyiez là où je veux en venir.
    Je précise tout de même que pour les autres services proposés par certaines classes filles, mais pas toutes, j'utiliserai le pattern NVI.
    Mais pour ceux présentés ici, je ne suis convaincu de son utilité.


    Pour le second cas, disons que l'on dispose d'une hiérarchie de foncteurs.
    Quoi ? Cela vous paraît étrange ?
    Hum...

    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
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    struct fonct1
    {
     
        virtual
        return_type operator () (arguments_type... args)
        {
            (...)
            f1(...);
            (...)
            f2(...);
            (...)
        }
     
      protected:
        void f1(...);
     
        void f2(...);
     
    }; // struct fonct1
     
     
    struct fonct2 : public fonct1
    {
     
        virtual
        return_type operator () (arguments_type... args)
        {
            (...)
            f1(...);
            (...)
            f2(...);
            (...)
        }
     
    }; // struct fonct1
     
     
    //******************//
     
    struct fonct3
    {
     
        virtual
        return_type operator () (type arg0, arguments_type... args);
     
    }; // struct fonct3
     
     
    struct fonct4 : public fonct3
    {
     
        virtual
        return_type operator () (type arg0, arguments_type... args)
        {
            switch (arg0) {
                case ...:
                    (...)
                    break;
     
                case ...:
                    (...)
                    break;
     
                (...)
     
                default:
                    return fonct3::operator()(arg0, args...);
            }
            (...)
        }
     
    }; // struct fonct4
    Dans le premier exemple, où la seconde classe utilise des éléments (données ou fonctions) de la première, on aurait pu en faire des classes sœurs, certes.
    Et dans ce cas, la classe mère aurait été attribuée d'un opérateur () virtuel pur.
    Mais du coup, la question se pose aussi.
    NVI ou pas NVI ?

    Pour le second exemple, la seconde classe ajoute des comportements spécifiques pour des valeurs particulières à la première.

    Bon, je me doute bien que dans un contexte autre que celui des foncteurs, on utiliserait sans hésiter le pattern NVI.
    Alors pourquoi faire une exception ?
    Eh bien, la principale raison d'être d'un foncteur est l'opérateur ().
    À moins que l'opération intrinsèque soit vraiment complexe, on n'écrit pas grand chose à côté.
    Bien sûr, une classe fille hérite de l'opérateur de la classe mère, s'il n'est pas redéfini.
    Mais je me demandais si, par convention, on ne se contentait pas de rendre l'opérateur virtuel si l'on pouvait avoir besoin de le redéfinir dans des classes filles.
    Bien entendu, comme précisé en début de message, on reste dans le cadre où il n'y a ni précondition, ni postcondition.


    S'il y a des cas connus où il n'y a pas besoin du pattern NVI, je suis tout ouïe...


    [Edit]
    Citation Envoyé par Steph_ng8 Voir le message
    Il ne me reste plus qu'à modifier ma question.
    À quoi faut-il réfléchir quand on veut savoir si le pattern NVI se justifie ?
    [/Edit]

  2. #2
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Une petite question en plus, pas vraiment liée au pattern NVI, mais à la virtualité...

    Lorsqu'une fonction membre est déclarée virtual, alors sa redéfinition dans les classes filles l'est forcément.
    Employé ce mot-clé pour les classes filles est donc a priori inutile, sauf que ça apporte une information pour le programmeur (qu'il n'a pas besoin d'aller chercher à la base de l'héritage).

    C'est également vrai pour le destructeur.
    Mais on peut se retrouver dans un cas où l'on n'a pas besoin d'écrire le destructeur (constructeur par défaut inchangé, constructeur par copie et opérateur d'assignation non définie dans la classe mère, corps du destructeur vide).
    Sans héritage, il serait conseillé de ne pas écrire le destructeur.
    Mais dans ce cas précis, a-t-on intérêt à l'écrire, pour bien indiquer la présence de l'héritage (sachant qu'il peut y avoir des fonctions membres virtuelles dans cette classe) ?

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Dans l'absolu, si ta classe peut être héritée, tu dois mettre ton destructeur en virtuel, même s'il ne fait rien, et même si tu n'as pas de fonctions virtuelles.

    Car si un client crée une classe dérivée, y colle des données membres, manipule des instances de cette nouvelle classe comme des pointeurs vers la classe de base, et les supprime, tu te retrouves avec des fuites de mémoire.

  4. #4
    Membre émérite Avatar de Steph_ng8
    Homme Profil pro
    Doctorant en Informatique
    Inscrit en
    Septembre 2010
    Messages
    677
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Informatique

    Informations forums :
    Inscription : Septembre 2010
    Messages : 677
    Par défaut
    Désolé, visiblement je n'ai pas été clair.
    Le destructeur dont je parle est celui d'une classe dont la classe mère possède un destructeur virtuel.

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Novembre 2004
    Messages
    2 766
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2004
    Messages : 2 766
    Par défaut
    Dans ce cas là, le destructeur n'a rien de particulier : tu rajoutes virtual si tu l'estimes nécessaire quant à la compréhension du code. C'est aussi une question de préférences personnelles...

  6. #6
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Citation Envoyé par Steph_ng8 Voir le message
    Désolé, visiblement je n'ai pas été clair.
    Le destructeur dont je parle est celui d'une classe dont la classe mère possède un destructeur virtuel.
    Non, sauf si tu veux pouvoir dériver également ta classe fille.

    Quand tu as :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class A {};
    class B : public A {};
    le problème se présente quand tu écris :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    A* a = new B();
    delete a;
    delete appelle ~A et pas ~B. Donc il faut un destucteur virtuel pour A pour que delete appelle le bon destructeur. Par contre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    B* b = new B();
    delete b;
    delete appelle bien ~B, qui appellera lui même ~A donc pas de problème, même sans mettre le destructeur de B en virtuel.

  7. #7
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    Citation Envoyé par gbdivers Voir le message
    Non, sauf si tu veux pouvoir dériver également ta classe fille.
    Tu parles pour des raisons de documentation de la classe fille? Il me semblait que virtual se propageait vers les filles?

Discussions similaires

  1. $db->query ne marche pas dans tous les cas
    Par bigorre1000 dans le forum Zend_Db
    Réponses: 8
    Dernier message: 22/07/2008, 19h50
  2. Réponses: 1
    Dernier message: 17/03/2008, 20h29
  3. [CloseWindow] Quitte dans tous les cas
    Par michaeljeru dans le forum AWT/Swing
    Réponses: 2
    Dernier message: 05/05/2007, 14h45
  4. Priorité aux familles dans tous les cas
    Par aline921 dans le forum Congés
    Réponses: 6
    Dernier message: 06/03/2007, 16h53
  5. Réponses: 23
    Dernier message: 11/04/2006, 17h33

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