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 :

Intérêt du concept de final


Sujet :

Langage C++

  1. #1
    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 Intérêt du concept de final
    Bonjour,
    Je me demande l'intérêt d'un mot clé de type 'final' ? Le problème n'est pas ce qu'il peut signifier syntaxiquement : qu'une classe ne peut plus être dérivée ou qu'une fonction virtuelle ne puisse plus être spécialisée. Mais dans quel cas il est intéressant de 'figer' ainsi l'héritage ?
    C'est une notion qui existe dans d'autres langages (final en java, sealed en C#), et qui va être introduite en C++0x avec les attributs. Donc je me dis que ça doit certainement avoir un intérêt. Le seul avantage qui me vienne spontanément est lié à des histoires d'optimisation où on peut s'appuyer dessus pour remplacer des résolutions dynamiques d'appels par des résolutions statiques.
    En lisant Penser en Java, (passons sur le final sur des variables qui sont l'équivalent d'un const), final pour une méthode est vraiment présenté dans un objectif d'optimisation (et d'inlining). L'argumentation pour une classe est assez ténue et remet en avant l'idée d'optimisation.
    Si on ne veut pas qu'une classe serve de base à l'héritage (public), alors aujourd'hui on définit un destructeur public et non virtuel. C'est vrai que c'est implicite et que ça n'empêchera pas quelqu'un de faire quand même un héritage. Dès qu'une classe a une fonction virtuelle (à fortiori un destructeur virtuel public), on conçoit cette classe comme pouvant servir de base à l'héritage. Pourquoi à un moment vouloir figer ce comportement ?
    Mais là où j'ai vraiment du mal à voir un intérêt, c'est final sur une méthode.
    J'aimerais savoir si c'est une notion qui vous manque. Si vous avez des exemples où elle est utile et même nécessaire.

  2. #2
    Membre confirmé Avatar de Lavock
    Profil pro
    Inscrit en
    Octobre 2009
    Messages
    560
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2009
    Messages : 560
    Points : 633
    Points
    633
    Par défaut
    Pour une classe, je ne dis que +1.

    Pour une méthode, non ce n'est pas un manque critique, on fait avec. En plus, rare sont les fois ou c'est vraiment la seul et unique solution à mon avis.

    Comme dit sur l'autre file, je vais essayer de faire un truc plus construit.

    Un cas pas anodin, et celui d'une bête factorisation de code.

    Par exemple, sur une bête hiérarchie 3 niveaux :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    struct A {
    //...
    }
    struct B : A {
    //...
    }
    struct C : B {
    //...
    }
    struct D : B {
    //...
    }
    Imaginons que B possède, dans sa semie-spécialisation, de quoi satisfaire une fonction virtuelle. Le fait est que cette fonction est virtuelle, donc PEUT être redéfinie dans C et D. Or les informations nécessaire sont seulement dans B et devrait, à priori, être private. Ce qui ne peut être le cas si on veux que C ou D influe sur la fonction. En réalité, dans se cas, B ne sert à rien, mais il y a gros risque de duplication de code >< !

    L'avantage de final, c'est que je pourrait sans risque factoriser un comportement commun, tout en permettant d'influencer celui-ci, et ceci sans exposer les "détails d'implémentations" de la classe intermédiare (b).


    Un exemple plus concret peut être se pattern décorateur présenter dans un autre file (modifier et (un peu) corriger) :
    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 Drawable {
      std::vector<std::pair<Color, std::vector<Point> > > point_set;
     
    private:
      virtual void vDraw();
      virtual Drawable* vDeleteSomethingSpecial(const std::string& reco) {return this};
     
    public:
      Drawable* deleteSomethingSpecial(const std::string& reco){
        return vDeleteSomethingSpecial(reco);
      }
      void draw() {
        vDraw();
      }
      virtual ~Drawable();
     
    };
    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
     
    class SpecialDrawable : Drawable{
    private:
       Drawable* base; //details d'implémentation
    private:
       virtual bool isThis(const std::string& reco) = 0;//comportement infactorisable
     
       virtual void doSomethingSpecial() = 0;//comportement infactorisable
     
       virtual void vDraw() [[final]] { //pas sûr de la syntaxe -_-'
         base->draw(); //Je suis sûre qu'il n'y aura pas d'imper ici !
         doSomethingSpecial();
       }
       virtual Drawable* vDeleteSomethingSpecial(const std::string& reco) [[final]] {
          if(isThis(reco)) {
             Drawable* ret = base;
             base = 0; // là non plus
             delete this;
             return ret;
          }
          base = base->deleteSomethingSpecial(reco); //et ici non plus
          return *this;
       }
     
    public:
       SpecialDrawable(Drawable* _base): base(_base) {}
       virtual ~SpecialDrawable {
          delete base;
       }
    };
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    class DoSomethingGreat : SpecialDrawable {
       type name;
    private:
      virtual void doSomethingSpecial(); //comportement infactorisable
      virtual void isThis(const std::string& reco) {//comportement infactorisable
         return (reco == string("DoSomethingGreat"));
      }
    public:
      DoSomethingGreat();
      virtual ~DoSomethingGreat();
    };
    (désolé, j'ai pas verifié la syntaxe encore une fois ).

    Sans final, j'aurais du exposer mon implémentation au classe fille, qui soit aurait du penser à appeler le code parent, soit le forker. Le premier choix entraîne des erreurs bêtes difficilement debugable, le deuxième rend inutile la classe intermédiaire...

    Avec les final, tout le monde est plus tranquil.
    Bon, c'est un exemple de ce qu'on peut, pas forcément LE truc à faire.

Discussions similaires

  1. Finally, quel est l'intérêt en fin de compte ?
    Par Djobird dans le forum Langage
    Réponses: 13
    Dernier message: 26/06/2009, 11h15
  2. Méthode Finalize et problème de conception
    Par phryos dans le forum Langage
    Réponses: 4
    Dernier message: 19/04/2006, 11h04
  3. [Conception] Performances : intérêt des vues ?
    Par Mr N. dans le forum PHP & Base de données
    Réponses: 10
    Dernier message: 20/10/2005, 13h46
  4. [J2SE] intérêt du final dans : fonction( final int arg) ?
    Par guile.rondins dans le forum Langage
    Réponses: 4
    Dernier message: 20/07/2005, 16h03
  5. [Concept] BD ou Gestion par fichier. Intérêt de la BD ?
    Par Cian dans le forum Décisions SGBD
    Réponses: 3
    Dernier message: 28/11/2002, 12h16

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