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 :

Adaptation nom de fonctions en fonction du contexte


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Mars 2007
    Messages
    134
    Détails du profil
    Informations forums :
    Inscription : Mars 2007
    Messages : 134
    Par défaut Adaptation nom de fonctions en fonction du contexte
    Bonjour, j'ai mis en place une class qui permet de gérer des points (2 coordonnées). Par contre, j'aimerais pouvoir adapter les fonctions d'accès en fonction du type de point. Par exemple, pour des longitudes/latitudes, utiliser get/set longitude/latitude. Si ce sont des coordonnées XY, utiliser get/set X/Y. La fonction appelée étant la même. Je peux faire cela avec de l'héritage mais est-ce que je ne suis pas en train de réduire les performances avec des "indirections" ? Est-ce que ceci est possible avec une autre approche (par exemple avec des "using") ? Merci d'avance !

    Mon exemple avec de l'héritage:
    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 pointBase {
    public:
      double getCoord1() const {return mC1;}
      double getCoord2() const {return mC2;}
      void setCoord1(const double c1) {mC1=c1;}
      void setCoord2(const double c2) {mC2=c2;}
    private:
      double mC1;
      double mC2;
    }
     
    class pointXY : public pointBase {
      double getX() const {return getCoord1();}
      double getY() const {return getCoord2();}
      void setX(const double c1) {setCoord1(c1);}
      void setY(const double c2) {setCoord2(c2);}
    }

  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,

    Déjà, la bonne question est : pourquoi veux tu placer tes deux points dans l'accessibilité privée

    Fais attention lorsque tu répondras à cette question, car, si la réponse ne contient pas le terme "invariant" (ou quelque chose d'approchant), tu risques fort d'avoir "tout faux"

    En effet, il ne suffit pas de placer les données dans l'accessibilité privée pour pouvoir se dire qu'on les "encapsule correctement". Surtout si on décide de fournir un accesseur / getter, ce qui peut encore se comprendre et un mutateur / setter, qui brise quant à lui totalement la notion même d'encapsulation.

    Car, ce qu'il faut comprendre, c'est que le but de l'encapsulation, c'est de "protéger" les données de manière à s'assurer que l'utilisateur de ta classe n'ira pas les modifier en risquant de leur donner une valeur invalide / incohérente.

    Or, lorsque tu décides d'exposer un mutateur / setter, tu dis -- en gros -- à l'utilisateur:
    Bon, tu t'occupes de calculer la nouvelle valeur, puis tu me la donne, et je la placerai au bon endroit
    Si bien que c'est alors à l'utilisateur de ta classe que revient la responsabilité de calculer cette nouvelle valeur, au risque d'oublier l'une ou l'autre obligation / restriction en le faisant, ce qui va bien sur totalement à l'encontre du principe d'encapsulation.

    Au final, si ta notion (PointBase dans ton code) n'est en fait "rien de plus" qu'un agrégat de données (comprends: plusieurs données qui sont rassemblées parce qu'elles "vont bien ensembles") dans le sens où l'on n'attend aucun service particulier de la part de cette notion, si ce n'est de donner un accès (sommes toutes "plein et entier") aux données qui la composent, pourquoi donc devrait-on se faire du mal à placer ces données dans une accessibilité restreinte et à rajouter un get et un set sur chacune d'elles Autant les laisser dans l'accessibilité publique, et laisser le soin à l'utilisateur d'en déterminer les valeurs "à sa guise", vu que c'est déjà ce qu'il fera, non

    De plus, ta classe PointBase a ce que l'on appelle une sémantique de valeur. Il n'y a donc aucune raison de faire en sorte de faire entrer cette classe dans une hiérarchie de classe "classique"
    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
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Je suis en phase.

    Si ta classe ne contient que 2 champs (x, y) et des fonctions get/set, alors utilise une simple structure avec 2 champs publiques.

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    En effet, l'exemple que tu fournis est à l'opposé de ce qui doit se faire. En privatisant tes champs, tu t'interdis un accès direct. Et ici tu perds le fait que ton pointBase est une entité de stockage, par exemple tu ne peux pas écrire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    PointXY  ptxy{3.14, 42.};  // impossible!
    auto [x,y] = ptxy;         // impossible!
    .

    Pour répondre à tes questions:
    - l'utilisation de using, sert à récupérer des fonctionnalité des classes de base avec l'intérêt d'avoir le même nom, hors tu veux justement changer le nom.
    - il n'y a pas du tout de perte de performance dans l'exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    double  x = ptxy.getX();  // utilise PointXY::getX() en inline donc fait:
    double  x = ptxy.getCoord1();  // utilise  pointBase::getCord1() en inline donc fait:
    double  x = ptxy.mC1;     // on n'a pas le droit d'écrire cette ligne (c'est private) mais c'est ce qui est effectivement fait.

  5. #5
    Membre confirmé
    Inscrit en
    Mars 2007
    Messages
    134
    Détails du profil
    Informations forums :
    Inscription : Mars 2007
    Messages : 134
    Par défaut
    Bonjour à tous les 3 et merci pour vos conseils. Je pensais que passer par des getters/setters était une bonne pratique mais vos explications sont très claires et je me rends compte que j'ai encore des choses à apprendre ! Je vais repenser mon approche :-)

  6. #6
    Expert confirmé
    Homme Profil pro
    Analyste/ Programmeur
    Inscrit en
    Juillet 2013
    Messages
    4 771
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Analyste/ Programmeur

    Informations forums :
    Inscription : Juillet 2013
    Messages : 4 771
    Par défaut
    Personne ne te l'a dit mais c'est aussi 1 question de performances.
    Lorsqu'on fait 1 classe point, c'est pour faire 1 collection de points (de beaucoup de points) et toutes les indirections (mutateurs et accesseurs en particulier) peuvent être source d'1 perte de performance "temps".

    Sinon pour répondre à la question, je vois 3 solutions ... mais mes exemples sont idéaux et peuvent être difficiles à utiliser (et tu peux les mixer) :
    • template
    • member pointers. Attention, depuis le C++ moderne (après C++03) on peut peut-être faire différemment. Et la syntaxe est lourde parce que le C++ est très très rigoureux sur les types.
    • template + enum

    Par contre , comme les autres intervenants te l'ont dit, ces solutions touchent la conception de ton code/ classe ... ce qui semble inaproprié pour 1 classe point qui est + 1 DTO ("Data Transfer Object") ou sémantique de valeur pour parler en C++ et non en Java

    Exemple template :
    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
    73
    74
    75
    76
    77
    #include <iostream>
    #include <cstdlib>
     
     
    class t_coords
    {
    public: // maybe a vector here
     
        double coord01;
        double coord02;
    };
     
    class t_coords_xy
    {
    public:
     
        void get_coord01 () {
            std::cout << "t_coords_xy::get_coord01" << std::endl;
        }
     
        void get_coord02 () {
            std::cout << "t_coords_xy::get_coord02" << std::endl;
        }
    };
     
     
    class t_coords_polar
    {
    public:
     
        void get_coord01 () {
            std::cout << "t_coords_polar::get_coord01" << std::endl;
        }
     
        void get_coord02 () {
            std::cout << "t_coords_polar::get_coord02" << std::endl;
        }
    };
     
     
    // Solution 1 : template
     
    template<class T> class t_point
    {
    public:
     
        void get_coord01 () {
            coords.get_coord01();
        }
     
        void get_coord02 () {
            coords.get_coord02();
        }
     
    private:
     
        T coords;
    };
     
     
    int main ()
    {
    //  Solution 1 : template
        std::cout << "Solution 1 : template" << std::endl;
     
        t_point<t_coords_xy> point_xy;
        t_point<t_coords_polar> point_polar;
     
        point_xy.get_coord01();
        point_xy.get_coord02();
     
        point_polar.get_coord01();
        point_polar.get_coord02();
     
     
        return EXIT_SUCCESS;
    }

    Exemple member pointers :
    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
    73
    74
    #include <iostream>
    #include <cstdlib>
     
     
    // Solution 2 : member pointers
     
    class t_point
    {
    public:
     
        enum t_type { type_xy = 0, type_polar };
     
        t_point (t_point::t_type type = t_point::type_xy) {
            switch (type) {
            case type_xy:
                get_coord01 = &t_point::get_xy_coord01;
                get_coord02 = &t_point::get_xy_coord02;
                break;
            case type_polar:
                get_coord01 = &t_point::get_polar_coord01;
                get_coord02 = &t_point::get_polar_coord02;
                break;
            }
        }
     
        typedef void (t_point::*t_get_coord01) ();
     
        typedef void (t_point::*t_get_coord02) ();
     
        t_get_coord01 get_coord01;
     
        t_get_coord02 get_coord02;
     
     
    protected:
     
        void get_xy_coord01 () {
            std::cout << "t_point::get_xy_coord01" << std::endl;
        }
     
        void get_xy_coord02 () {
            std::cout << "t_point::get_xy_coord02" << std::endl;
        }
     
        void get_polar_coord01 () {
            std::cout << "t_point::get_polar_coord01" << std::endl;
        }
     
        void get_polar_coord02 () {
            std::cout << "t_point::get_polar_coord02" << std::endl;
        }
    };
     
     
    int main ()
    {
    //  Solution 2 : member pointers
        std::cout << "Solution 2 : member pointers" << std::endl;
     
        t_point point_xy(t_point::type_xy);
        t_point point_polar(t_point::type_polar);
     
        t_point::t_get_coord01 coord01_func;
        t_point::t_get_coord02 coord02_func;
     
        coord01_func = point_xy.get_coord01; (point_xy.*coord01_func) ();
        coord02_func = point_xy.get_coord02; (point_xy.*coord02_func) ();
     
        coord01_func = point_polar.get_coord01; (point_polar.*coord01_func) ();
        coord02_func = point_polar.get_coord02; (point_polar.*coord02_func) ();
     
     
        return EXIT_SUCCESS;
    }
    Exemple template + enum :
    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
    #include <iostream>
    #include <cstdlib>
     
     
    // Solution 3 : template + enum
    typedef enum e_coords_xy    { G_COORDS_XY=1 }    g_t_coords_xy;
    typedef enum e_coords_polar { G_COORDS_POLAR=2 } g_t_coords_polar;
     
    template<int value_type> class t_point
    {
    public:
     
        t_point() : type(value_type) {}
     
        void get_coord01 () {}
        void get_coord02 () {}
     
    private:
     
        int type;
    };
     
    template<> class t_point<G_COORDS_XY>
    {
    public:
     
        void get_coord01 () {
            std::cout << "t_point<xy>::get_coord01" << std::endl;
        }
     
        void get_coord02 () {
            std::cout << "t_point<xy>::get_coord02" << std::endl;
        }
    };
     
    template<> class t_point<G_COORDS_POLAR>
    {
    public:
     
        void get_coord01 () {
            std::cout << "t_point<polar>::get_coord01" << std::endl;
        }
     
        void get_coord02 () {
            std::cout << "t_point<polar>::get_coord02" << std::endl;
        }
    };
     
     
    int main ()
    {
    //  Solution 3 : template + enum
        std::cout << "Solution 3 : template + enum" << std::endl;
     
        t_point<G_COORDS_XY> point_xy;
        t_point<G_COORDS_POLAR> point_polar;
     
        point_xy.get_coord01();
        point_xy.get_coord02();
     
        point_polar.get_coord01();
        point_polar.get_coord02();
     
     
        return EXIT_SUCCESS;
    }

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

Discussions similaires

  1. Réponses: 4
    Dernier message: 27/06/2007, 13h38
  2. Cherche nom de fonction
    Par DeezerD dans le forum Langage
    Réponses: 4
    Dernier message: 15/10/2005, 01h14
  3. Réponses: 2
    Dernier message: 04/10/2005, 15h13
  4. Nom de fonction dynamique
    Par gege2061 dans le forum C
    Réponses: 2
    Dernier message: 21/06/2005, 14h44
  5. Evaluation d'un nom de fonction
    Par uaz dans le forum Général Python
    Réponses: 1
    Dernier message: 04/08/2004, 11h16

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