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 :

Constantes, globales et portée, des mots qui font rêver.


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2010
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2010
    Messages : 33
    Par défaut Constantes, globales et portée, des mots qui font rêver.
    Bonjour,

    J'ai lu un peu partout qu'utiliser les macros pour définir ses constantes n'était pas la meilleure solution en C++. Il est de manière générale plutôt recommandé d'utiliser une variable const.
    e.g.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    // Pas bien
    #define TEXT L"je suis théoriquement une mauvaise constante C++ :'("
    // Bien
    const wchar_t* text L"je suis théoriquement une bonne constante C++ :D";
    Oui mais voilà, ça me fait me poser tout un tas de question auxquelles ni mes recherches sur le web ou dans mes livres n'apportent de réponse. Je vous les liste donc point par point. Petite précision, j'utilise le C++11 dans la mesure ou mon compilateur (Visual c++ 2012) me le permet :

    1) J'ai lu que seules les constantes ayant un type intégral étaient valides en C++, et que c'est même l'une des limitations que le nouveau mot clé const expr du C++11 permettait de contourner. Seulement mon compilateur ne le supporte pas encore, et j'ai pourtant besoin de déclarer des constantes qui ne sont pas de type intégral comme des double ou des wchar_t. Comment suis-je censé m'y prendre dans ce cas là?

    2) Où dois-je déclarer mes constantes? Dans les fichiers source? Les headers? Est-ce que ça change quelque chose?

    3) Quelle est la différence concrète entre une constante et une globale? Sont-elles évaluées à la compilation?

    4) J'essaye d'utiliser le plus possible les unnamed namespaces en remplacement du mot clé static comme préconisé dans le nouveau standard du C++11. Mais je me demande si c'est vraiment nécessaire lorsque l'on est déjà dans un namespace comme dans l'exemple suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    namespace rwl
    {
    namespace // Est-ce vraiment utile?
    {
    int useless_stuffs_in_this_code_count = 999999;
    }
     
    // Tout plein de fonctions dégueulasses qui utilisent ma variable.
     
    }
    Car il me semble bien qu'une variable déclarée dans un namespace a déjà sa portée limitée à ce dernier.

    5) Un cas concret maintenant. J'ai eu besoin de déclarer une constante accessible partout dans mon espace de nom "rwl". Je m'y suis prit de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
    namespace // Dans le doute...
    {
    const wchar_t*	logfile_rwl = L"Logfiles/rwl.log";
    }
    }
    Ainsi, je n'ai qu'à inclure ce header pour bénéficier de ma constante là où j'en ai besoin. Est-ce propre? Existe t-il une meilleure solution?

    Voilà, en vous remerciant d'avance pour votre aide.

  2. #2
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Citation Envoyé par RT222 Voir le message

    1) J'ai lu que seules les constantes ayant un type intégral étaient valides en C++, et que c'est même l'une des limitations que le nouveau mot clé const expr du C++11 permettait de contourner. Seulement mon compilateur ne le supporte pas encore, et j'ai pourtant besoin de déclarer des constantes qui ne sont pas de type intégral comme des double ou des wchar_t. Comment suis-je censé m'y prendre dans ce cas là?
    Totalement faux.

    Je te fais un resume:

    1. N'importe quel type peut etre const en C++.
    2. En revanche, seul les integral peuvent etre utilise en paramettre template.
    3. const n'est pas synonyme d'immutable. Il faut bien comprendre la difference, meme si definir un objet static const rends l'objet theoriquement immutable (un const cast dessus t'emmene a "undefined behaviour" land)
    4. constexpr permet d'indiquer au compilateur qu'une fonction peut etre utilise pour generer une valeur a la compilation, ce qui sous entends que le resultat de la fonction peut etre utilise comme une constante immutable.
    5. constexpr peut aussi etre utilse pour les constructeurs, et donc les objets.
    6. Quand on declare un variables const, si c'est des integral, on peut les mettre dans des headers sans souci. Si ce sont des types utilisateurs (class/struct), les definir dans un header reviens a les redefinir dans tous les cpp qui incluent le header, et donc cela provoque une erreur de "multiple definition de symbole" au link.
    7. Pour eviter le probleme de 6, il suffit de faire la declaration de la constante dans le header, marke comme extern, puis la definition dans un seul unique cpp.
    8. Cela est valide pour les constantes definis dans les classes aussi (mais on n'a pas besoin de extern dans ce cas.)

    Je crois que j'ai fait le tour pour les bases. Dans ton cas, tu as juste a mettre la declaratoin dans un header, la definition dans un cpp.

    2) Où dois-je déclarer mes constantes? Dans les fichiers source? Les headers? Est-ce que ça change quelque chose?
    Oui, je crois que j'ai repondu dans ma precedente fonction.
    Garde en tete que:

    1. seul les fichiers cpp sont compiles
    2. les headers sont du contenu copie/colle dans les cpp qui l'incluent

    En partant de ca tu realises vite que si tu definirs quelque chose dans un header, il se retrouve duplique. C'est pas grave pour les entier, ca linkera pas pour les autres types.

    3) Quelle est la différence concrète entre une constante et une globale? Sont-elles évaluées à la compilation?
    Ce sont deux choses completement orthogonales.

    Seul ce qui est marque constexpr est evalue a la compilation SI les paramettres passes a la fonction (ou au constructeur) sont accessible a la compilation aussi.

    Theoriquement, certains objets marque static const (ou const mais en namespace anonyme - a confirmer) peuvent etre optimises par la compilateur, mais ce n'est pas garanti du tout.


    4) J'essaye d'utiliser le plus possible les unnamed namespaces en remplacement du mot clé static comme préconisé dans le nouveau standard du C++11. Mais je me demande si c'est vraiment nécessaire lorsque l'on est déjà dans un namespace comme dans l'exemple suivant :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    namespace rwl
    {
    namespace // Est-ce vraiment utile?
    {
    int useless_stuffs_in_this_code_count = 999999;
    }
     
    // Tout plein de fonctions dégueulasses qui utilisent ma variable.
     
    }
    Car il me semble bien qu'une variable déclarée dans un namespace a déjà sa portée limitée à ce dernier.
    Le namespace anonyme ne va pas changer le nom du namesapce au dessus. Ce qu'il fait c'est rendre son contenu accessible uniquement au cpp qui est compile. Cela reviens effectivement a ajouter static a tes variables, mais ca fait aussi isoler les fonctions qui sont interne a ton cpp.

    Note aussi que cela indique au compilateur qu'il ne doit surtout pas permettre de link avec d'autre cpp pour ces fonctions et donnees. En gros tu indique au compilateur de ne pas exposer les synboles dans le namespace anonyme de maniere a les isoler totalement et les reserver a l'usage d'un seul et unique cpp.

    Je crois pas que ce soit utile dans un header.

    5) Un cas concret maintenant. J'ai eu besoin de déclarer une constante accessible partout dans mon espace de nom "rwl". Je m'y suis prit de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
    namespace // Dans le doute...
    {
    const wchar_t*	logfile_rwl = L"Logfiles/rwl.log";
    }
    }
    Ainsi, je n'ai qu'à inclure ce header pour bénéficier de ma constante là où j'en ai besoin. Est-ce propre? Existe t-il une meilleure solution?
    Mets la definition dans ton cpp, le namespace anonyme n'a aucun interet puisque tu DOIS exposer le symbole pour l'utiliser ailleur. Sinon tu vas provoquer des duplications de nom.....


    ...quoi que j'ai un doute, il se peut aussi que cela soit accepte par le compilateur mais generer un objet par cpp. JE vais tester voir.


    Sinon:

    header

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
          extern const wchar_t*	logfile_rwl; // declaration
     
    }
    cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    #include "config.h"
    namespace rwl
    {
           const wchar_t*	logfile_rwl = L"Logfiles/rwl.log"; // definition
     
    }
    (moi je l'aurai mis dans un sous namespace reserve aux constantes mais bon ca depends de ce que tu fais - j'aurais aussi mis les constantes en GRANDES_LETTRES_AVEC_UNDERSCORE mais c'est a toi de voir).


    EDIT> J'ai oublie les 'extern'!

  3. #3
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Apparement GCC 4.7 et VS2012 acceptent les multiple definitions si c'est dans un namespace anonymes. Pas certain que ce soit standard, mais je vais demander.

  4. #4
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    J'ai demande la: https://groups.google.com/a/isocpp.o...on/IQOJOP_vM5M


    Donc en gros

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /*
    ** config.hpp
    */
    #pragma once
    namespace rwl
    {
    namespace // Dans le doute...
    {
    const wchar_t*	logfile_rwl = L"Logfiles/rwl.log";
    }
    }
    Est tout a fait valide et fera ce que tu penses.

    La subtilite c'est que l'objet (ou plutot un clone) va etre definis une fois par cpp qui inclus le fichier.

    A priori c'est un moyen inferieur de declarer des constantes (pour les non-constantes je vois bien une utilite...) compare au couble header+cpp sauf qu'on a un seul fichier a gerer pour les definitions.


    EDIT:

    On Mon, Feb 4, 2013 at 11:23 AM, Johannes Schaub
    <schaub.johannes@googlemail.com> wrote:

    Unnamed namespaces should only be used in a header for data (but i still prefer just declaring them "const"). And only when the data in it is const.

    Mutable data wont carry modifications outside a TU which is bad if the declaration was in a header.

    And for functions, making them static or in an unnamed namespace easily will bloat the executable. Either make it inline, so that you have only one function. Or define it in the cpp file (which we want to avoid for the purpose of discussion).

    And for classes, the reasoning is the same as fir nonconst data. If they are ina header, they ought to have extern linkage and be the same for every inclusion. Otherwise, put it not in a header.

    Another good argument for not splitting const data up into a cpp file is avoiding the static initialization order fiasco that happens when you use the data value in the initialization of another nonlocal nonmember object.
    Donc en gros, ta definition est bonne et a meme un avantage concernant l'ordre d'initialization. Cela dis, la taille de l'exe va dependre des optimizations au link...

  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
    Je rappelle que la traduction d'"integral", c'est "entier(e)"...

  6. #6
    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,

    Et, ce qui n'a pas été écrit jusqu'à présent... :

    Il faut rappeler que les variables (constantes ou non) globales, c'est mal.

    La raison est bien simple: une globale est, comme son nom l'indique, disponible depuis "n'importe où".

    Déjà, il est très rare d'avoir réellement besoin de quelque chose depuis "n'importe où": au pire, on a généralement besoin de quelque chose à "plusieurs endroits clairement identifiés", mais ce n'est pas pareil

    Ensuite, si une variable globale n'est pas constante, le fait qu'elle soit accessible depuis n'importe où fait que l'on perd tout contrôle sur les modifications que peut subir cette variable, ce qui rend le débuggage franchement difficile si, pour une raison ou une autre, la valeur ne correspond pas à ce que l'on croit qu'elle devrait être
    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

  7. #7
    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 Klaim Voir le message
    Apparement GCC 4.7 et VS2012 acceptent les multiple definitions si c'est dans un namespace anonymes. Pas certain que ce soit standard, mais je vais demander.
    Au niveau des compilateurs, un namespace anonyme défini dans une unité de compilation crée un pseudo-namespace nommé de manière unique (ce nom ne t'es pas accessible). Du coup, deux namespace anonyme dans deux unités de compilation sont nécessairement différents, et le même symbole peut y être défini plusieurs fois (l'ODR est quand même respecté).
    [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.

  8. #8
    Membre Expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Par défaut
    Citation Envoyé par Emmanuel Deloget Voir le message
    Au niveau des compilateurs, un namespace anonyme défini dans une unité de compilation crée un pseudo-namespace nommé de manière unique (ce nom ne t'es pas accessible). Du coup, deux namespace anonyme dans deux unités de compilation sont nécessairement différents, et le même symbole peut y être défini plusieurs fois (l'ODR est quand même respecté).
    C'est ce qu'on m'a confirme dans les messages suivants.

  9. #9
    Membre averti
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2010
    Messages
    33
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2010
    Messages : 33
    Par défaut
    Tout me paraît tellement plus clair maintenant! Un grand merci à vous pour vos réponses, et particulièrement à Klaim. Vous ne pouvez pas savoir à quel point ça m'aide.

    Concernant la constante que j'utilise, je comprends l'avertissement de Koala01 et j'essaye d'éviter au maximum les constantes globales (et je m'interdis les globales tout court). D'ailleurs celle que j'ai montrée ici est la seule que j'utilise dans mon programme. J'en ai eu besoin car elle me sert à configurer le fichier dans lequel les messages d'erreur loggés depuis mon espace de nom rwl seront écrits. Et comme je logge un petit peu partout...

    j'aurais aussi mis les constantes en GRANDES_LETTRES_AVEC_UNDERSCORE mais c'est a toi de voir
    Dans ma norme ce sont uniquement les directives préprocesseurs qui sont en majuscule, car j'avais lu un article intéressant qui le recommandait et avait des arguments fort pertinents pour le justifier.
    [EDIT] : Je l'ai retrouvé : http://blog.ploki.info/post/25160737...-en-majuscules


    J'aurais juste deux questions supplémentaires :

    1) Comme, pour des raisons imposées par l'architecture de mon programme, je suis obligé de définir ma constante uniquement dans le header, et que je n'aime pas trop l'idée qu'elle soit dupliquée dans chaque cpp où elle est incluse (ça me paraît sale), ne serait-il pas mieux dans ce cas précis d'utiliser une macro?

    2) J'ai des fonctions que je souhaite rendre inaccessible en dehors de la TU où elles sont définies. J'ai tenté de procéder ainsi, comme je l'aurais fait avec une variable :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    /*
    ** source.cpp
    */
    namespace rwl
    {
    namespace
    {
    void fonction();
    }
    void fonction()
    {
    }
    }
    Sauf que ça ne compile pas : "unresolved external symbol". En revanche, si j'enlève le namespace anonyme et que j'utilise le mot clé static, le programme compile parfaitement, et mes fonctions sont effectivement inaccessibles en dehors de leur TU. Ce comportement est-il normal?

Discussions similaires

  1. supprimer des mots qui commencent avec un chiffre
    Par sitws dans le forum Débuter avec Java
    Réponses: 4
    Dernier message: 20/02/2010, 00h56
  2. [XL-2003] Liste des cellules qui font référence à une cellule donnée
    Par CUCARACHA dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 03/02/2010, 16h29
  3. Des div qui font ceux qu'ils veulent
    Par sacados1 dans le forum Mise en page CSS
    Réponses: 3
    Dernier message: 20/11/2007, 22h47
  4. Comment éviter que google répete des mots qui sont dans la description du meta tag
    Par tese84 dans le forum Balisage (X)HTML et validation W3C
    Réponses: 2
    Dernier message: 18/09/2006, 07h55

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