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 :

Struct propre à une classe


Sujet :

Langage C++

  1. #1
    Membre éclairé
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Par défaut Struct propre à une classe
    Bonjour.

    Soit une classe MaClasse qui doit gérer une std::map d'éléments constitués par un double + une std::string + un booléen. La question simple que je me pose est : quelle est la méthode la plus "propre" et "classique" de faire ça :
    - créer une classe juste pour un élément
    - créer un structure élément comprenant mon double + std::string + booléen
    - y'a-t-il moyen de rendre la déclaration de cette structure "interne" à la classe (en ce sens qu'il n'y a que cette classe qui a besoin de cette structure) et dans ce cas là quelle est la syntaxe à utiliser ? (déclaration dans le header je suppose mais comment ? (je pense qu'un exemple serait le mieux pour que je comprenne))

    Merci

  2. #2
    Membre chevronné
    Femme Profil pro
    Développeur Java
    Inscrit en
    Décembre 2009
    Messages
    236
    Détails du profil
    Informations personnelles :
    Sexe : Femme

    Informations professionnelles :
    Activité : Développeur Java
    Secteur : Finance

    Informations forums :
    Inscription : Décembre 2009
    Messages : 236
    Par défaut
    Bonjour,
    Je pense que d'un point de vue objet le plus propre est de créer une classe, même si je doute que ce soit le plus performant. Apres si tu souhaite que celle ci reste propre à la classe qui l'utilise rien ne t'empeche de créer ta classe en interne.

  3. #3
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Bonjour.

    Soit une classe MaClasse qui doit gérer une std::map d'éléments constitués par un double + une std::string + un booléen. La question simple que je me pose est : quelle est la méthode la plus "propre" et "classique" de faire ça :
    - créer une classe juste pour un élément
    - créer un structure élément comprenant mon double + std::string + booléen
    - y'a-t-il moyen de rendre la déclaration de cette structure "interne" à la classe (en ce sens qu'il n'y a que cette classe qui a besoin de cette structure) et dans ce cas là quelle est la syntaxe à utiliser ? (déclaration dans le header je suppose mais comment ? (je pense qu'un exemple serait le mieux pour que je comprenne))

    Merci
    Le mieux est le premier choix : créer une classe, qui se vera dotée de ses propres comportements. La déclaration de class pouvant être imbriquée, on aurait :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    class A
    {
    public: // pour rendre le type public
      class B
      {
      };
    private:
      std::mon_conteneur<B> m_var;
    };
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  4. #4
    Membre éclairé
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Par défaut
    Merci beaucoup.

    Par contre, j'aimerai comprendre pourquoi il vaut mieux utiliser une classe qu'un structure ? (si vous avez bien suivi, c'est juste pour stocker un double, une std::string et un booléen)

  5. #5
    Membre très actif
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Par défaut
    En C++ il n'y a aucune différence entre une classe et une structure à part le fait que dans le premier cas les membres sont privés par défaut et dans le second cas les membres sont publics par défaut.
    Mis à part le cas où il ne s'agit que de stocker des données toutes publiques (où l'on utilise plus volontiers le terme structure) dans les autres cas l'usage courant est de recourir à une classe, mais ça ne change absolument rien par ailleurs.
    Voir par exemple :
    http://publib.boulder.ibm.com/infoce...%2Fcplr054.htm

  6. #6
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par Kaluza Voir le message
    Merci beaucoup.

    Par contre, j'aimerai comprendre pourquoi il vaut mieux utiliser une classe qu'un structure ? (si vous avez bien suivi, c'est juste pour stocker un double, une std::string et un booléen)
    3 données qui ne sont liées par aucune relation n'ont pas à être stockées ensembles. Si les données sont stockées ensemble, j'en déduis qu'il y a de grandes chances qu'il existe une relation entre ces trois données - relation qui dicte le domaine de validité de chacune des données en fonction des autres, probablement.

    Une telle relation est nommée "invariant" dans le jargon abscons des informaticiens. Si L'une des données est publique, alors une partie de l'invariant est exposé, et peut potentiellement être modifié de manière à ce que l'état du trio de variable ne soit plus cohérent (on dit que l'invariant est violé).

    Le fait d'utiliser une classe (avec des méthodes) plutôt qu'une structure (généralement sans méthode, même si en C++, la différence entre classe et structure ne se situe pas du tout à ce niveau ; cf post de ptyxs), c'est qu'on va probablement mettre les données privées et fournir des comportements qui peuvent agir sur l'invariant tout en vérifiant qu'il est toujours valide, quel que soit l'instant où il est examiné par les autres objets.

    Violà pourquoi, dans ton cas, je préconise une classe plutôt qu'une structure.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  7. #7
    Membre éclairé
    Homme Profil pro
    Doctorant en Astrophysique
    Inscrit en
    Mars 2009
    Messages
    312
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Doctorant en Astrophysique
    Secteur : Enseignement

    Informations forums :
    Inscription : Mars 2009
    Messages : 312
    Par défaut
    Merci pour ce paragraphe qui clarifie les choses.
    Effectivement si le domaine de validité d'un des éléments dépend de l'autre, il faudrait utiliser des éléments privés d'une classe.
    En ce qui me concerne, ce n'est pas le cas (il s'agit de 3 champs indépendants qui renseignent sur la nature de quelque chose, champs que j'ai besoin de stocker dans un conteneur std).

    Je pense donc que dans l'état je peux utiliser une structure (si il y a interdépendance des domaines de validité des éléments, j'utiliserai évidemment une classe)

    Merci encore

  8. #8
    Membre très actif
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Par défaut
    Enfin bon, comme dit plus haut et explicité dans mon lien, j'insiste : en C++ structure et classe c'est blanc bonnet et bonnet blanc (une structure de C++ ce n'est donc pas une structure au sens du C ! une structure du C++ peut avoir des membres privés, tout comme une classe).

  9. #9
    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
    Bonjour,
    Je vais m'éloigner un peu du sujet, mais j'aimerais avoir quelques précisions.

    Citation Envoyé par Emmanuel Deloget Voir le message
    3 données qui ne sont liées par aucune relation n'ont pas à être stockées ensembles. Si les données sont stockées ensemble, j'en déduis qu'il y a de grandes chances qu'il existe une relation entre ces trois données - relation qui dicte le domaine de validité de chacune des données en fonction des autres, probablement.

    Une telle relation est nommée "invariant" dans le jargon abscons des informaticiens. Si L'une des données est publique, alors une partie de l'invariant est exposé, et peut potentiellement être modifié de manière à ce que l'état du trio de variable ne soit plus cohérent (on dit que l'invariant est violé).

    Le fait d'utiliser une classe (...) plutôt qu'une structure (...), c'est qu'on va probablement mettre les données privées et fournir des comportements qui peuvent agir sur l'invariant tout en vérifiant qu'il est toujours valide, quel que soit l'instant où il est examiné par les autres objets.

    Violà pourquoi, dans ton cas, je préconise une classe plutôt qu'une structure.
    Et pour std::pair<T, U> ?
    Les deux données sont publiques, et peuvent être modifiées indépendamment l'une de l'autre, et par n'importe qui.
    Dans ce cas, c'est donc à l'utilisateur qu'il incombe de vérifier que l'invariant est valide, c'est bien ça ?
    Dans le cas où quelle que soit la combinaison des données, l'invariant reste valide, est-ce important/nécessaire de ne pas l'exposer ?

    Un petit exemple concret.
    On dispose d'une classe atome.
    Un littéral est soit un atome (littéral positif), soit la négation d'un atome (littéral négatif).
    Plutôt que de réécrire une classe, j'utilise un std::pair<atome, bool> (en simplifiant).
    Ainsi, si un littéral l est positif (resp. régatif), alors l.second == true (resp. l.second == false).

    Si l'on change la valeur de l'atome sans changer le signe, et inversement, le littéral reste valide et cohérent ; on a simplement changé la valeur du littéral.

    Dans ce cas précis, aurais-tu tout de même utilisé une classe et masqué les données ?

  10. #10
    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,
    Citation Envoyé par Steph_ng8 Voir le message
    Bonjour,
    Je vais m'éloigner un peu du sujet, mais j'aimerais avoir quelques précisions.


    Et pour std::pair<T, U> ?
    Les deux données sont publiques, et peuvent être modifiées indépendamment l'une de l'autre, et par n'importe qui.
    A priori, non, vu que tu veux que seule la classe qui dispose des informations sache qu'il y a une structure sous-jascente

    En outre, on peut présumer que {1.24,"tutu",true} représente une entieté particulière qui est différente de l'entité {1.24,"tutu",false} ou de l'entité {1.24,"tata",true} !

    Les trois représentent peut etre une entité "valide" et qui se trouve dans un "état cohérent", mais, si tu en viens à envisager de maintenir une collection d'éléments composés de trois valeurs, c'est sans doute qu'il y a une relation entre ces trois valeurs!!!

    Autrement, tu gérerait une collection de double d'un coté, une collection de chaies de caractères au milieu et une collection de booléen de l'autre coté
    Dans ce cas, c'est donc à l'utilisateur qu'il incombe de vérifier que l'invariant est valide, c'est bien ça ?
    A partir du moment où plusieurs données sont liées, ce n'est jamais à l'utilisateur de vérifier si les invariants restent valides, parce que, s'il ne le fait pas (et tu peux t'attendre à ce qu'il ne le fasse pas, par oubli ou par paresse ) tu risque de te retrouver avec des objet inconsistants, dans un état aberrant, et de ne pas t'en rendre compte...

    Et ca, c'est le pire qui puisse arriver, car le plus diffile à tracer pour réoudre le problème
    Dans le cas où quelle que soit la combinaison des données, l'invariant reste valide, est-ce important/nécessaire de ne pas l'exposer ?
    c'est moins grave de l'exposer si l'on reste dans l'idée que c'est un invariant "à usage interne exclusif" d'une classe particulière (que tu crées une structre imbriquée).

    Mais ce n'est malgré tout pas une bonne idée pour autant
    Un petit exemple concret.
    On dispose d'une classe atome.
    Un littéral est soit un atome (littéral positif), soit la négation d'un atome (littéral négatif).
    Plutôt que de réécrire une classe, j'utilise un std::pair<atome, bool> (en simplifiant).
    Ainsi, si un littéral l est positif (resp. régatif), alors l.second == true (resp. l.second == false).

    Si l'on change la valeur de l'atome sans changer le signe, et inversement, le littéral reste valide et cohérent ; on a simplement changé la valeur du littéral.

    Dans ce cas précis, aurais-tu tout de même utilisé une classe et masqué les données ?
    Presonnellement, oui...

    Pour plusieurs raisons dont la principale étant qu'il sera beaucoup plus facile de se rendre compte que l'on parle bel et bien d'un litéral et non de n'importe quoi d'autre qui pourrait être représené sous la forme d'une paire atome /booléen.

    La deuxième raison est que, bien qu'un litéral puisse etre valide quelque soit la valeur du booléen, je ne voudrais sans doute pas que "n'importe qui" aille changer cette valeur "n'importe comment" (autrement, mon atome d'hydrogène risque d'être pris pour une atome d'hélium ) et que je voudrais sans doute m'assurer que cela se fasse "dans les règles"

    Enfin, je me dis que je pourrais parfaitement décider de manipuler une pair atome / booléen pour la représentation réelle aujourd'hui et une structure (non issue de std::pair) demain...

    Si tout le monde n'accède à mon litéral que par un nombre limité de fonctions représentant l'interface publque, je n'aurai que ce nombre limité de fonctions à modifier si je prends une telle décision, alors que si je laisse n'importe qui accéder librement à l'atome et au booléen, j'aurai beaucoup plus d'endroit à aller modifier, sans compter ceux que l'utilisateur aura lui-même créé, et sur lesquels je n'ai aucun pouvoir

    Bon, je t'accorde que je violerais allègrement l'OCP (Open Close Principle) avec cette approche, mais j'aime autant "prévoir le coup"
    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

  11. #11
    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
    Hum, je vois...
    Tout cela me laisse penser que la classe std::pair ne devrait pas exister...
    ... en tout cas pas sous cette forme.
    Peut-être est-il prévu de l'abandonner au profit de std::tuple ?
    Quoique c'est pareil, on peut modifier chaque valeur indépendamment des autres...

    [Edit]À moins qu'il y ait une sorte de contrat tacite :
    « Au moment où je vous ai transmis cette paire, elle était valide et cohérente ; à vous de vous débrouiller pour conserver ces propriétés. »
    Hum... ça revient à ce que je disais plus haut...[/Edit]

    Bon, et en supposant qu'une telle structure interne n'est pas accessible de l'extérieur, et que la classe englobante ne ferait jamais rien d'illégal ou incohérent (disons même qu'elle ne peut pas le faire), n'y a-t-il pas un surcoût à utiliser une classe complète plutôt qu'une simple structure ?

  12. #12
    Membre très actif
    Profil pro
    professeur des universités à la retraite
    Inscrit en
    Août 2008
    Messages
    364
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : professeur des universités à la retraite

    Informations forums :
    Inscription : Août 2008
    Messages : 364
    Par défaut
    Citation Envoyé par Steph_ng8 Voir le message
    n'y a-t-il pas un surcoût à utiliser une classe complète plutôt qu'une simple structure ?
    comme dit et répété : une classe et une structure, c'est fondamentalement la même chose !
    Je ne vois que de la confusion à gagner à t'entêter à distinguer ces deux notions de la manière dont tu le fais...
    Parle plutôt d'une classe dont tous les membres sont publics vs une classe avec des membres privés...

  13. #13
    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
    Citation Envoyé par Steph_ng8 Voir le message
    Hum, je vois...
    Tout cela me laisse penser que la classe std::pair ne devrait pas exister...
    ... en tout cas pas sous cette forme.
    Peut-être est-il prévu de l'abandonner au profit de std::tuple ?
    Quoique c'est pareil, on peut modifier chaque valeur indépendamment des autres...

    [Edit]À moins qu'il y ait une sorte de contrat tacite :
    « Au moment où je vous ai transmis cette paire, elle était valide et cohérente ; à vous de vous débrouiller pour conserver ces propriétés. »
    Hum... ça revient à ce que je disais plus haut...[/Edit]
    Absolulment pas: paire (ou tuple, c'est blanc bonnet et bonnet blanc à ce point de vue ) fournit, justement, une association forte permettant soit de fournir deux représentations différentes de la même chose (un numéro identifiant de manière unique et une structure complexe, par exemple) qui pourra, par exemple, etre utilisée dans un tableau associatif "clé / valeur" soit de créer une structure "flottante" permettant de n'obtenir qu'une information particulière tout en gardant le lien entre deux information produisant une donnée complexe...

    Il faut de plus noter que la pair peut réellement avoir sémantique d'entité tout en permettant, malgré tout, une certaine "souplesse" dans cette sémantique : bien souvent, on décidera que la modification d'une des parties (first, ou certaines parties de second) provoquera la création d'une nouvelle paire
    Bon, et en supposant qu'une telle structure interne n'est pas accessible de l'extérieur, et que la classe englobante ne ferait jamais rien d'illégal ou incohérent (disons même qu'elle ne peut pas le faire), n'y a-t-il pas un surcoût à utiliser une classe complète plutôt qu'une simple structure ?
    Pas énorme, à inexistant, surtout si les fonctions que tu fournis pour manipuler cette classe sont inllinées, ce qui a de fortes chances d'arriver, vu que tu ne fournira sans doute que les mutateurs et les accesseurs
    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

  14. #14
    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
    Citation Envoyé par ptyxs Voir le message
    comme dit et répété : une classe et une structure, c'est fondamentalement la même chose !
    Je ne vois que de la confusion à gagner à t'entêter à distinguer ces deux notions de la manière dont tu le fais...
    Parle plutôt d'une classe dont tous les membres sont publics vs une classe avec des membres privés...
    Désolé, il y avait trop de choses implicites dans mes propos.

    Quand je dit « structure simple », je veux dire un struct dans lequel on se contente de déclarer les champs (donc publics).

    Quant à « classe complète », je pense à une class qui contient les champs (privés), mais également des constructeurs, des accesseurs, éventuellement des mutateurs, des fonctions de vérification de cohérence, etc.

    Je te rassure, j'ai eu de vrais cours de C++, et je connais la différence entre struct et class.

  15. #15
    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
    Citation Envoyé par koala01 Voir le message
    Absolulment pas: paire (ou tuple, c'est blanc bonnet et bonnet blanc à ce point de vue ) fournit, justement, une association forte permettant soit de fournir deux représentations différentes de la même chose (un numéro identifiant de manière unique et une structure complexe, par exemple) qui pourra, par exemple, etre utilisée dans un tableau associatif "clé / valeur" soit de créer une structure "flottante" permettant de n'obtenir qu'une information particulière tout en gardant le lien entre deux information produisant une donnée complexe...
    Je ne comprends pas.
    S'il y a une dépendance forte, il ne faut pas en exposer les membres !
    J'ai l'impression que tu te contredis (même si je me doute que c'est plutôt moi qui ne vois pas où tu veux en venir...).
    À moins que tu veux dire std::pair ne devrait être utilisée qu'en interne ?

    Et qu'entends-tu par « structure "flottante" »
    (Rien à voir avec le mot-clé struct, ça j'ai compris.)

    Citation Envoyé par koala01 Voir le message
    Il faut de plus noter que la pair peut réellement avoir sémantique d'entité tout en permettant, malgré tout, une certaine "souplesse" dans cette sémantique : bien souvent, on décidera que la modification d'une des parties (first, ou certaines parties de second) provoquera la création d'une nouvelle paire
    C'est là-dessus que j'étais parti pour mes littéraux.

    Citation Envoyé par koala01 Voir le message
    Pas énorme, à inexistant, surtout si les fonctions que tu fournis pour manipuler cette classe sont inllinées, ce qui a de fortes chances d'arriver, vu que tu ne fournira sans doute que les mutateurs et les accesseurs
    Bon, soit...


    Autre exemple.
    Je dois utiliser mes littéraux conjointement avec un booléen en tant que clé d'un tableau associatif (héritage privé de std::map).
    En interne, j'utilisais encore une std::pair (eh oui, ça en faisait 3 en tout...).
    L'utilisateur n'y a jamais accès, excepté à travers les itérateurs.
    (Et encore, il me semble que j'ai limité les itérateurs publics aux itérateurs constants.)
    Mais de toute façon, la paire n'est visible que constante, vu que c'est la clé.

    Dans ce cas, je peux garder ma paire ?
    De toute façon, je ne saurais pas nommer la classe liant ces deux données.
    Et puis elle n'aurait pas vraiment de sens.
    En plus elle ne serait jamais utilisée ailleurs.

  16. #16
    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
    Citation Envoyé par Steph_ng8 Voir le message
    Je ne comprends pas.
    S'il y a une dépendance forte, il ne faut pas en exposer les membres !
    J'ai l'impression que tu te contredis (même si je me doute que c'est plutôt moi qui ne vois pas où tu veux en venir...).
    Peut etre as tu déjà entendu parler de std::map

    Il s'agit d'un tableau associatif qui fait le tri sur une donnée qui permet d'en identifier une autre de manière unique.

    Les deux choses (la clé et la valeur) représentent fondamentalement la même chose, et, si tu change la clé, c'est que tu manipules, forcément, une autre valeur (l'inverse étant également vrai dans une certaine mesure: si tu modifie certaine données de la valeur, tu devrais sans doute te retrouver avec une autre clé )

    Il y a donc un lien fort entre les deux, parce que l'élément first de la pair représente fondamentalement la même chose que l'élément second!

    Si les deux éléments sont publics, c'est essentiellement pour ne pas rajouter une couche de complexité aux algorithmes génériques qui sont utilisés par la std::map


    À moins que tu veux dire std::pair ne devrait être utilisée qu'en interne ?
    A peu de choses près, oui, ou sous la forme d'un itérateur, le plus souvent constant...

    Car, si tu modifie l'élément qui sert de clé pour le tri, tu place toute la map dans un état incohérent, du fait qu'un objet va se retrouver à une place qui n'est pas la sienne.

    La seule solution pour s'en sortir est alors de veiller à supprimer la paire en question et à en réinsérer une autre

    Et qu'entends-tu par « structure "flottante" »
    (Rien à voir avec le mot-clé struct, ça j'ai compris.)
    En fait, c'est aussi une struct (la pair est créée avec le mot clé struct ) mais ce sera peut etre plus compréhensible si on observe la tuple.

    Peut etre as tu étudié un peu les bases de données, et, dans ce cas, le mot tuple ne t'est pas tout à fait inconnu...

    La tuple, en base de données, et un ensemble hétérogène d'éléments "de base" représentant un enregistrement (complexe) dans une table.

    Le tout est alors plus important que l'ensemble de ce qui le compose, car une personne est l'ensemble de son nom, de son prénom, de son adresse (qui se subdivise elle-meme en plusieurs informations particulières ) et de son age (par exemple), mais chaque information doit être accessible de manière distincte.

    La paire et la tuple fournies par le standard ne font qu'implémenter ce genre de concept!!!

    D'ailleurs, c'est bien simple, l'ensemble de la STL ne fait que fournir une implémentation générique de concepts qui sont tout aussi génériques (et qui se doivent donc de respecter des règles génériques )
    C'est là-dessus que j'étais parti pour mes littéraux.
    Le problème de la paire, c'est que les deux parties sont intimement liées, mais que les règles sont définies par "quelqu'un d'autre" (par la norme, en fait), et que ces règles:
    • risquent fort de ne pas correspondre à tes besoin
    • ne peuvent pas être changées


    Un bête exemple: l'opérateur < qui sert de base pour le tri est défini de manière à invoquer l'opérateur < exclusivement sur... la première donnée (first), indépendamment de la deuxième, et surtout, indépendamment du type des deux données (ce qui implique d'ailleurs que l'opérateur < doit etre défini pour le type de first :p) .

    Si tu veux modifier cette règle, tu devra carrément passer par un foncteur qui agira, le cas échéant, de manière différente sur base du type réel des deux données représentées.

    Si, par contre, tu crées ta propre structure (classe) toi même, tu garde toute lattitude pour définir ton propre opérateur < (et tous les autres, si besoin en est) pour faire en sorte, par exemple, que tes littéraux soient d'abord triés sur ceux dont le booléen est à false, puis, pourquoi pas, sur le poids volumique de l'atome (ou sa charge électrique, ou son nom ou... n'importe quelle combinaison envisageable )

    Et tu arrivera à le faire beaucoup plus facilement avec une "structure à toi" qu'avec une paire
    Autre exemple.
    Je dois utiliser mes littéraux conjointement avec un booléen en tant que clé d'un tableau associatif (héritage privé de std::map).
    Une chance que tu précises que c'est un héritage privé, parce que j'étais sur le point de pousser de hauts cris

    Mais il faut être conscient du fait que l'héritage, qu'il soit public ou privé, est la relation la plus forte qui puisse exister entre deux types (la relation EST-UN pour l'héritage public et la relation "EST IMPLEMENTE EN TERME DE pour l'héritage privé) !!!

    Il faut aussi être conscient que, 95 fois sur cent, un héritage privé sera avantageusement remplacé par... une composition classique (par le simple fait de fournir un membre dont le type est celui avec lequel on envisage l'héritage privé)!

    Ce n'est pas pour rien que de nombreux langages refusent purement et simplement l'héritage privé
    En interne, j'utilisais encore une std::pair (eh oui, ça en faisait 3 en tout...).
    L'utilisateur n'y a jamais accès, excepté à travers les itérateurs.
    (Et encore, il me semble que j'ai limité les itérateurs publics aux itérateurs constants.)
    Mais de toute façon, la paire n'est visible que constante, vu que c'est la clé.

    Dans ce cas, je peux garder ma paire ?
    Il est peut etre temps de te rappeler bien fort un principe de base (sorti tout droit de l'XP, mais bon :p) KISS (Keep It Simple, Stupid)!!!

    Si je suis d'accord avec le fait qu'il faut refuser le syndrome du NIH (Not Implemented Here), il faut garder un tout petit peu de sens pratique!!!

    Imagines tu le code qu'il te faudra écrire pour, simplement, arriver à obtenir un itérateur si tu n'utilises que les std::pair

    Cela pourrait donner quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map<std::pair<std::pair<bool, atom>,std::pair<truc, machin> >::/* const_*/iterator it= begin()
    Après l'avoir écrit trois fois, tu regrettera amèrement de ne pas avoir créé une structure ou une classe pour relier ces différentes données!!!

    Et je ne te parles même pas du risque d'erreur auquel tu t'expose lors de l'écriture de ce machin (j'ai vraiment du réfléchir à deux fois pour etre sur que je ne créais pas une clé à trois composant et une valeur à un seul... en espérant que tu voulais une clé bi composant et une valeur elle aussi bi composant )
    De toute façon, je ne saurais pas nommer la classe liant ces deux données.
    Demande toi simplement à quoi elle sert, et utilise cela comme nom

    Si tu arrives à faire que le nom correspond exactement à ce que tu attends de ta classe, tu as gagné
    Et puis elle n'aurait pas vraiment de sens.
    Pouruqoi donc

    Elle représente une responsabilité unique (même si on monte déjà dans la granularité de la responsabilité), elle a donc tout son sens, au contraire
    En plus elle ne serait jamais utilisée ailleurs.
    Et alors où est le problème ce qui importe, c'est qu'elle fournisse les services que tu en attends et qu'elle te permette d'assurer le respect de l'ORP (One Responsability Principle) !

    Personne n'a jamais dit qu'une classe ou qu'une structure n'avait réellement de sens que si elle était utilisée en plusieurs endroits
    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

  17. #17
    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
    Ok pour std::pair.

    J'ai effectivement étudié les bases de données, et ton laïus sur les tuples me parle complètement.
    Par contre, je ne vois toujours pas ce que tu veux dire par « structure "flottante" »...


    Citation Envoyé par koala01 Voir le message
    Un bête exemple: l'opérateur < qui sert de base pour le tri est défini de manière à invoquer l'opérateur < exclusivement sur... la première donnée (first), indépendamment de la deuxième, et surtout, indépendamment du type des deux données (ce qui implique d'ailleurs que l'opérateur < doit etre défini pour le type de first :p) .
    Ah non, l'opérateur < sur les paire effectue une comparaison lexicographique, donc prend bien en compte les deux données.
    Cela me convient très bien pour l'instant, mais c'est vrai que rien ne me dit qu'à l'avenir je n'aurai pas envie (besoin ?) de changer.
    J'étais déjà pratiquement convaincu d'utiliser ma propre classe, alors ça c'est le coup de grâce...


    Citation Envoyé par koala01 Voir le message
    Une chance que tu précises que c'est un héritage privé, parce que j'étais sur le point de pousser de hauts cris

    Mais il faut être conscient du fait que l'héritage, qu'il soit public ou privé, est la relation la plus forte qui puisse exister entre deux types (la relation EST-UN pour l'héritage public et la relation "EST IMPLEMENTE EN TERME DE pour l'héritage privé) !!!

    Il faut aussi être conscient que, 95 fois sur cent, un héritage privé sera avantageusement remplacé par... une composition classique (par le simple fait de fournir un membre dont le type est celui avec lequel on envisage l'héritage privé)!

    Ce n'est pas pour rien que de nombreux langages refusent purement et simplement l'héritage privé
    Conceptuellement, cela me paraît logique de faire hériter mon conteneur de std::map.
    La relation EST-UN me paraît tout à fait appropriée.
    J'aurais utilisé un héritage public si les destructeurs des conteneurs de la STL étaient virtuels...
    Ai-je tort ?
    Et de quels avantages parles-tu pour la composition classique ?


    Citation Envoyé par koala01 Voir le message
    Si je suis d'accord avec le fait qu'il faut refuser le syndrome du NIH (Not Implemented Here), il faut garder un tout petit peu de sens pratique!!!
    Euh...


    Citation Envoyé par koala01 Voir le message
    Imagines tu le code qu'il te faudra écrire pour, simplement, arriver à obtenir un itérateur si tu n'utilises que les std::pair

    Cela pourrait donner quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::map<std::pair<std::pair<bool, atom>,std::pair<truc, machin> >::/* const_*/iterator it= begin()
    Après l'avoir écrit trois fois, tu regrettera amèrement de ne pas avoir créé une structure ou une classe pour relier ces différentes données!!!
    Premièrement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    std::map< std::pair< std::pair<atome, bool>, bool >, std::set<int> >
    // Du coup, la 3e paire est mapped_type :
    std::pair< std::pair< std::pair<atome, bool>, bool >, std::set<int> >
    Ensuite, je ne suis pas complètement fou !
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct litteral : public std::pair<atome, bool> { ... };
    class raison : private std::set<int> { ... };
    class raison_litteral : private std::map<std::pair<litteral, bool>, raison> { ... };

    Pour la paire du dernier exemple (littéral, booléen), j'ai oublié de préciser une chose.
    En fait, je me retrouve un peu dans le même cas que Kaluza, à savoir que je dois stocker trois valeurs.
    Mais l'accès à la troisième se fait par l'intermédiaire des deux autres.

    Pour être clair, reprenons la référence aux bases de données.
    Dans ma table, je stocke un triplet (litteral, booléen, raison).
    La clé primaire correspond aux deux premières valeurs.

    C'est la raison pour laquelle je disais qu'une classe contenant le littéral et le booléen n'aurait pas vraiment de sens, et que je ne vois pas comment l'appeler.

  18. #18
    Membre Expert

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2007
    Messages
    1 895
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Septembre 2007
    Messages : 1 895
    Par défaut
    Citation Envoyé par Steph_ng8 Voir le message
    Bonjour,
    Je vais m'éloigner un peu du sujet, mais j'aimerais avoir quelques précisions.


    Et pour std::pair<T, U> ?
    Les deux données sont publiques, et peuvent être modifiées indépendamment l'une de l'autre, et par n'importe qui.
    Dans ce cas, c'est donc à l'utilisateur qu'il incombe de vérifier que l'invariant est valide, c'est bien ça ?
    Dans le cas où quelle que soit la combinaison des données, l'invariant reste valide, est-ce important/nécessaire de ne pas l'exposer ?

    Un petit exemple concret.
    On dispose d'une classe atome.
    Un littéral est soit un atome (littéral positif), soit la négation d'un atome (littéral négatif).
    Plutôt que de réécrire une classe, j'utilise un std::pair<atome, bool> (en simplifiant).
    Ainsi, si un littéral l est positif (resp. régatif), alors l.second == true (resp. l.second == false).

    Si l'on change la valeur de l'atome sans changer le signe, et inversement, le littéral reste valide et cohérent ; on a simplement changé la valeur du littéral.

    Dans ce cas précis, aurais-tu tout de même utilisé une classe et masqué les données ?
    Dans ce cas précis, tu te retrouve exactement dans le même cas que la classe std::complex : deux valeurs largement indépendantes l'une de l'autre, mais qui, prises dans leur ensemble, signifie quelque chose. Idem pour un vecteur2d, vecteur3d, une matrice, ...

    La notion d'objet vient alors du fait que certains opérations sont définies sur ces valeurs : sans les incorporer dans une classe, tu dois définir ces opérations de manière externe, et les typer faiblement (elle vont accepter tous les arguments du type pair<atome,bool>, même si le bool en question représente autre chose que le signe (par exemple, il peut représenter l'appartenance à un ensemble particulier ; j'en sais rien, je ne suis pas assez calé en math pour comprendre exactement ce que tu dis). Sans avoir typé de manière forte ta pair, tu perds une partie non négligeable de sa sémantique, et cette perte peut entraîner des erreurs au niveau du code en permettant par exemple une utilisation non prévue ou carrément fausse de la valeur.

    Un exemple parmi d'autres : une classe triplet<T> qui définit trois valeurs du type T. Un vecteur3d est un triplet<float>, de même qu'une couleur. Additionner deux triplet<T> n'a du sens que si les deux représente des valeurs issues du même ensemble (vector3d ou couleur), mais le code lui-même n'empêche pas l'addition d'un vector3d et d'une couleur s'il se base uniquement sur triplet<T>.

    Pour autant, ça ne signifie pas nécessairement que les membres de la classe remplaçant pair<atome,bool> doivent être privés. La classe n'a pas d'invariant (autre que l'invariant de la classe atome) car toute valeur est valide. Si la valeur du bool avait dépendu de la valeur de atome (ou l'inverse), alors on aurait eu tout intérêt à les mettre en privé, mais ce n'est pas le cas visiblement dans ton exemple.

    Ceci dit, mettre les valeurs en public expose l'implémentation. Si demain tu décide de changer la façon de stocker l'objet atome, cela aura un impact non négligeable sur tout le code client. En mettant les membres en privé, cela t'autorise à les modifier sans pour autant changer l'interface de la classe. Par exemple (et c'est un exemple volontairement foireux parce que je ne sais pas ce que tu entends par atome).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class litteral
    {
      atome m_atome;
      bool m_sign;
    public:
      const atome& value() const { return atome; }
      bool sign() const { return m_sign; }
    };
    Peut devenir

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // un litteral dont la valeur dépends de t
    class litteral
    {
      float m_factor;
      bool m_sign;
    public:
      // bien sûr, on ne peut pas renvoyer un const atome&, mais
      // ça change très peu de chose à l'utilisation
      atome value() const { return atome(m_factor, get_current_simulation_time()); }
      bool sign() const { return m_sign; }
    };
    L'interface de la classe ne change pas, malgré le changement d'implémentation. Si les membres sont publics, alors tu forces le code client à faire le calcul à chaque fois. Et si tu utilisais std:::pair, te voilà réduit à changer tout le code client pour t'adapter à la nouvelle représentation.
    [FAQ des forums][FAQ Développement 2D, 3D et Jeux][Si vous ne savez pas ou vous en êtes...]
    Essayez d'écrire clairement (c'est à dire avec des mots français complets). SMS est votre ennemi.
    Evitez les arguments inutiles - DirectMachin vs. OpenTruc ou G++ vs. Café. C'est dépassé tout ça.
    Et si vous êtes sages, vous aurez peut être vous aussi la chance de passer à la télé. Ou pas.

    Ce site contient un forum d'entraide gratuit. Il ne s'use que si l'on ne s'en sert pas.

  19. #19
    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
    Citation Envoyé par Emmanuel Deloget Voir le message
    Dans ce cas précis, tu te retrouve exactement dans le même cas que la classe std::complex : deux valeurs largement indépendantes l'une de l'autre, mais qui, prises dans leur ensemble, signifie quelque chose.
    Exactement.

    Citation Envoyé par Emmanuel Deloget Voir le message
    Si demain tu décide de changer la façon de stocker l'objet atome, cela aura un impact non négligeable sur tout le code client.
    Je m'en suis rendu compte en passant de atome const * à std::reference_wrapper<atome const>...

    Pour le reste, merci pour les explications.
    Je vois un peu mieux ton point de vue.
    Ceci dit, tu prêches un (désormais) convaincu...

  20. #20
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    J'aurais un avis plus nuancé. Pour une classe métier, utilisée à plusieurs endroits dans le code, une "vraie" classe bien encapsulée et tout me semble en effet justifiée.

    Pour une classe purement technique (par exemple une étape intermédiaire de la lecture d'un fichier, avant de stocker ça dans une classe métier, ou au moment de s'interfacer avec du code base niveau, ou bien encore le cas de std::pair, où on est à un niveau si bas qu'il n'y a de toute façon pas vraiment de comportement à encapsuler), une "simple structure" (avec éventuellement un constructeur) me semble suffisante.
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

Discussions similaires

  1. Réponses: 7
    Dernier message: 27/02/2012, 17h55
  2. Définitions enum et struct propres à une classe
    Par Kaluza dans le forum Langage
    Réponses: 5
    Dernier message: 26/07/2011, 08h58
  3. Accès aux attributs propres à une classe fille
    Par jamilya dans le forum Débuter avec Java
    Réponses: 2
    Dernier message: 24/12/2008, 15h06
  4. Réponses: 1
    Dernier message: 14/07/2008, 11h17
  5. python C API: convertir une struct C en Class python
    Par dmichel dans le forum Interfaçage autre langage
    Réponses: 11
    Dernier message: 25/06/2008, 16h30

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