2010-01-11 17 views
45

Je suis curieux de connaître les avantages/inconvénients de différentes options de déclaration et de définition de constantes en C++. Pendant très longtemps, je viens de les déclarer en haut du fichier d'en-tête avant la définition de classe:Où déclarer/définir des constantes de portée de classe en C++?

//.h 
const int MyConst = 10; 
const string MyStrConst = "String"; 
class MyClass { 
... 
}; 

Bien que ce pollue l'espace de noms global (que je sais est une mauvaise chose, mais je n'ai jamais trouvé une liste de raisons pour lesquelles elle est mauvaise), les constantes seront toujours étendues à des unités de traduction individuelles, de sorte que les fichiers qui n'incluent pas cet en-tête n'auront pas accès à ces constantes. Mais vous pouvez obtenir des collisions de noms si d'autres classes définissent une constante du même nom, ce qui n'est sans doute pas une mauvaise chose car cela peut être une bonne indication d'une zone qui pourrait être refactorisée.

Récemment, j'ai décidé qu'il serait préférable de déclarer des constantes spécifiques de classe à l'intérieur de la définition de la classe elle-même:

//.h 
class MyClass { 
    public: 
     static const int MyConst = 10; 
... 
    private: 
     static const string MyStrConst; 
... 
}; 
//.cpp 
const string MyClass::MyStrConst = "String"; 

La visibilité de la constante serait ajustée selon que l'on utilise uniquement en interne constante la classe ou est nécessaire pour d'autres objets qui utilisent la classe. C'est ce que je pense être la meilleure option en ce moment, principalement parce que vous pouvez garder les constantes de classes internes privées à la classe et toutes les autres classes utilisant les constantes publiques auraient une référence plus détaillée à la source de la constante (ex: MyClass: : MyConst). Il ne polluera pas non plus l'espace de noms global. Bien que cela ait l'inconvénient de nécessiter une initialisation non-intégrale dans le fichier cpp. J'ai également envisagé de déplacer les constantes dans leur propre fichier d'en-tête et de les placer dans un espace de noms au cas où une autre classe aurait besoin des constantes, mais pas de la définition de classe entière.

Juste à la recherche d'opinions et éventuellement d'autres options que je n'avais pas encore considérées.

Répondre

1

Vous pouvez les déclarer en tant que globaux dans le fichier C++, tant qu'ils ne sont pas référencés dans l'en-tête. Ensuite, ils sont privés à cette classe et ne pollueront pas l'espace de noms global.

+0

Oui, mais vous devez utiliser 'static' ou un espace de noms anonyme dans ce cas - si vous ne le faites pas, dans certaines implémentations vous polluez l'espace de noms global Utilisé par l'éditeur de liens et obtenir des collisions de noms sur les liens ... – hjhill

9

La pollution de l'espace de noms global est mauvaise car quelqu'un (par exemple l'auteur d'une bibliothèque que vous utilisez) peut vouloir utiliser le nom MyConst à d'autres fins. Cela peut entraîner de graves problèmes (bibliothèques qui ne peuvent pas être utilisées ensemble, etc.)

Votre deuxième solution est clairement la meilleure si les constantes sont liées à une seule classe. Si ce n'est pas si facile (pensez à des constantes physiques ou mathématiques sans lien avec une classe de votre programme), la solution d'espace de nommage est meilleure que cela. BTW: si vous devez être compatible avec les anciens compilateurs C++, n'oubliez pas que certains d'entre eux ne peuvent pas utiliser l'initialisation intégrale dans un fichier d'en-tête - vous devez initialiser dans le fichier C++ ou utiliser l'ancien truc enum dans ce cas.

Je pense qu'il n'y a pas de meilleures options pour les constantes - au moins ne peut pas penser à un au moment ...

2

Personnellement, j'utiliser votre deuxième approche; Je l'ai utilisé pendant des années, et ça marche bien pour moi. A partir d'un point de visibilité, j'aurais tendance à faire en sorte que les constantes privées soient au niveau du fichier statique car personne en dehors du fichier d'implémentation ne doit savoir qu'elles existent; Cela permet d'éviter les recompilations de réaction en chaîne si vous avez besoin de changer leur nom ou d'en ajouter de nouveaux car leur portée est la même que celle de leur utilisation ...

35

Votre affirmation que déclarer une constante non-intégrante en tant que membre de classe statique " au détriment de nécessiter une initialisation non-intégrale dans le fichier cpp "n'est pas exactement solide, pour ainsi dire.Cela nécessite une définition dans le fichier cpp, mais ce n'est pas un "détriment", c'est une question d'intention. objet namespace niveau const en C++ a une liaison interne par défaut, ce qui signifie que dans votre variante originale de la déclaration

const string MyStrConst = "String"; 

est équivalent à

static const string MyStrConst = "String"; 

-à-dire qu'il définira un objet MyStrConst indépendant dans chaque unité de traduction dans lequel ce fichier d'en-tête est inclus. Etes-vous conscient de cela? Était-ce votre intention ou non?

Dans tous les cas, si vous n'avez pas spécifiquement besoin d'un objet séparé dans chaque unité de traduction, la déclaration de MyStrConst constante dans votre exemple original n'est pas une bonne pratique. Normalement, vous auriez seulement mis une déclaration non-définition dans le fichier d'en-tête

extern const string MyStrConst; 

et de fournir une définition dans le fichier cpp

const string MyStrConst = "String"; 

assurant ainsi que l'ensemble du programme utilise le même objet constant . En d'autres termes, lorsqu'il s'agit de constantes non-intégrales, une pratique normale consiste à les définir dans un fichier cpp. Donc, peu importe la façon dont vous le déclarez (dans la classe ou à l'extérieur), vous devrez normalement toujours faire face au «détriment» d'avoir à le définir dans un fichier cpp. Bien sûr, comme je l'ai dit plus haut, avec les constantes d'espace de noms, vous pouvez vous débarrasser de ce que vous avez dans votre première variante, mais ce ne serait qu'un exemple de «codage paresseux».

De toute façon, je ne pense pas qu'il y ait une raison de compliquer le problème: si la constante a une "pièce jointe" évidente à la classe, elle devrait être déclarée en tant que membre de la classe.

P.S. Les spécificateurs d'accès (public, protected, private) ne contrôlent pas visibilité du nom. Ils contrôlent uniquement son accessibilité . Le nom reste visible dans tous les cas.

+0

Puis-je demander quelle est la différence entre la visibilité et l'accessibilité? Je pense qu'ils sont pareils, pouvez-vous donner un exemple? – toolchainX

+0

@toolchainX: Exemple: Une fonction membre peut être privée, mais la fonction peut être 'visible' au compilateur dans le sens où nous supposons que la définition existe et que le compilateur peut la 'voir'. Le compilateur n'applique simplement aucun accès [Erreur de compilation]. Supprimer la définition ou mettre la définition quelque part 'cachée' rendrait la fonction dans ces termes non 'visible' - même pour les fonctions membres - quel que soit l'accès [Erreur de lien]. – wardw

+0

Ce serait un détriment de le mettre dans le fichier .cpp car le compilateur ne peut pas aligner la constante car elle ne se trouve pas dans l'unité de traduction du compilateur. Si la constante est juste une classe enveloppant un entier, alors il serait préférable de l'avoir dans le fichier d'en-tête afin que le compilateur sache ce que sa valeur est dans toutes les unités de compilation. – qbt937

5

La pollution de l'espace de noms global devrait être évidente. Si j'inclue un fichier d'en-tête, je ne souhaite pas rencontrer ou déboguer des collisions de noms avec des constantes déclarées dans cet en-tête. Ces types d'erreurs sont vraiment frustrants et parfois difficiles à diagnostiquer. Par exemple, je devais une fois un lien contre un projet qui avait cette définition dans un en-tête:

#define read _read 

Si vos constantes sont la pollution de l'espace de noms, c'est l'espace de noms des déchets nucléaires. La manifestation de ceci était une série d'erreurs de compilation très bizarres se plaignant de manquer la fonction _read, mais seulement lors de la liaison avec cette bibliothèque. Nous avons finalement renommé les fonctions de lecture en quelque chose d'autre, ce qui n'est pas difficile mais devrait être inutile.

Votre deuxième solution est très raisonnable car elle met la variable dans la portée. Il n'y a aucune raison que cela doive être associé à une classe, et si j'ai besoin de partager des constantes entre classes, je vais déclarer des constantes dans leur propre espace de noms et dans leur propre fichier d'en-tête. Ce n'est pas génial pour la compilation, mais parfois c'est nécessaire.

J'ai également vu des gens mettre des constantes dans leur propre classe, qui peut être implémentée comme un singleton. Cela me semble fonctionner sans récompense, la langue vous fournit quelques facilités pour déclarer des constantes.

2

Si une seule classe va utiliser ces constantes, déclarez-les comme static const dans le corps de la classe. Si un groupe de classes liées utilise les constantes, déclarez-les soit dans une classe/structure qui ne contient que les constantes et les méthodes utilitaires, soit dans un espace de noms dédié. Par exemple,

namespace MyAppAudioConstants 
{ 
    //declare constants here 
} 

Si elles sont des constantes utilisées par l'ensemble de l'application (ou des morceaux importants de celui-ci), de les déclarer à l'intérieur d'un espace de noms dans un en-tête est (implicitement ou explicitement) inclus partout.

namespace MyAppGlobalConstants 
{ 
    //declare constants here 
} 
1

ne polluent pas l'espace de noms global, polluent localement.

namespace Space 
    { 
    const int Pint; 
    class Class {}; 
    }; 

Mais pratiquement ...

class Class 
    { 
    static int Bar() {return 357;} 
    };