web-dev-qa-db-fra.com

C++ Meilleures pratiques pour les constantes

J'ai toute une série de constantes auxquelles je veux accéder dans différentes parties de mon code, mais que je veux pouvoir accéder facilement à l'ensemble:

static const bool doX = true;
static const bool doY = false;
static const int maxNumX = 5;

etc. 

J'ai donc créé un fichier appelé "constants.h", je les ai tous collés et je les ai inclus dans tout fichier devant connaître une constante.

Le problème, c’est terrible pour les temps de compilation, car chaque fois que je change une constante, tous les fichiers référencés par constants.h doivent être reconstruits. (En outre, comme je le comprends bien, vu qu’elles sont statiques, je génère une copie de doX/doY/maxNumX dans le code chaque fois que j’inclue constants.h dans un nouveau fichier .cpp, ce qui entraîne des kilo-octets d’espace perdu dans la compilation. EXE - est-il possible de voir cela?).

Donc, je veux une solution. Une option qui ne consiste pas à "déclarer les constantes uniquement dans les fichiers qui les utilisent", si possible.

Aucune suggestion?

21
Ben Walker

La seule alternative est de rendre vos constantes extern et de les définir dans un autre fichier .cpp, mais vous perdrez le potentiel d'optimisation, car le compilateur ne saura pas quelle valeur ils ont lors de la compilation de chaque .cpp`. 

En passant, ne vous inquiétez pas de l'augmentation de la taille: pour les types intégraux, vos constantes sont susceptibles d'être directement insérées dans le code machine généré.

Enfin, cette variable static n'est pas nécessaire car, par défaut, les variables globales const sont static en C++.

9
Matteo Italia

Vous les déclarez en tant que extern dans l'en-tête et les définissez dans un fichier d'implémentation. 

Ainsi, lorsque vous souhaitez modifier leur valeur, vous modifiez le fichier d'implémentation et aucune nouvelle compilation complète n'est nécessaire. 

Le problème de votre variante n’est pas lié à la compilation, mais à la logique. Ils ne seront pas globaux puisque chaque unité de traduction aura sa propre copie de la variable.

MODIFIER:

La façon de le faire en C++ - serait de les envelopper dans une classe:

//constants.h
class Constants
{
public:
   static const bool doX;
   static const bool doY;
   static const int maxNumX;
}

//constants.cpp
const bool Constants::doX = true;
const bool Constants::doY = false;
const int Constants::maxNumX = 5;
7
Luchian Grigore

Je pense que votre hypothèse de base est éteinte.

Vos autres en-têtes sont généralement organisés en gardant ensemble ce qui fonctionne ensemble. Par exemple, une classe et ses méthodes associées ou deux classes fortement interconnectées.

Pourquoi regrouper toutes les constantes dans un seul en-tête? Cela n'a aucun sens. C'est à peu près une aussi mauvaise idée qu'un en-tête "global.h" d'inclure facilement chaque dépendance.

En général, les constantes sont utilisées dans un contexte particulier. Par exemple, une énumération utilisée comme indicateur pour une fonction particulière:

class File {
public:
  enum class Mode {
    Read,
    Write,
    Append
  };

  File(std::string const& filename, Mode mode);

  // ...
};

Dans ce cas, il est naturel que ces constantes résident dans le même en-tête que la classe à laquelle elles sont liées (et même dans la classe).

Les autres catégories de constantes sont celles qui imprègnent toute l'application. Par exemple:

enum class Direction {
  Up,
  Down,
  Right,
  Left,
  Forward,
  Backward
};

... dans un jeu où vous voulez exprimer le mouvement des objets en fonction de la direction dans laquelle ils sont dirigés.

Dans ce cas, créer un fichier d'en-tête pour cet ensemble spécifique de constantes est une bonne chose.

Et si vous êtes vraiment préoccupé par le regroupement de ces fichiers:

constants/
  Direction.hpp
  Sandwich.hpp
  State.hpp

Et vous éviterez parfaitement la question de la recompilation de l’ensemble de l’application lorsque vous ajouterez une constante ... mais si vous devez le faire, vous ne payez le coût qu’une seule fois, mieux qu’un design mal conçu, vous devrez le faire. vivre avec pour le reste de votre travail.

6
Matthieu M.

Quel est le problème avec cet usage?
Ne déclarez pas un type static dans le fichier d’en-tête, il ne fait pas ce que vous pensez qu'il fait. 

Lorsque vous déclarez un fichier statique dans le fichier d'en-tête, une copie de cette variable est créée dans chaqueunité de traduction (TU)où vous incluez ce fichier d'en-tête, SO each TU voit une variable différente, ce qui est opposé à votre attente d'avoir une variable globale.

Solution suggérée:
Vous devez les déclarer en tant que extern dans un fichier d’en-tête et les définir dans exactement un fichier cpp, tout en incluant l’en-tête avec extern dans chaque fichier cpp auquel vous souhaitez accéder.

Bonne lecture:
Comment utiliser extern?

3
Alok Save

Une autre approche qui convient le mieux aux temps de compilation (mais a un coût d’exécution minime) consiste à rendre les constantes accessibles via des méthodes statiques dans une classe.

//constants.h
class Constants
{
public:
  static bool doX();
  static bool doY();
  static int maxNumX();
};

//constants.cpp
bool Constants::doX() { return true; }
bool Constants::doY() { return false; }
int Constants::maxNumX() { return 42; }

L'avantage de cette approche est que vous ne recompilez tout si vous ajoutez/supprimez/modifiez la déclaration d'une méthode dans l'en-tête, tandis que modifier la valeur renvoyée par une méthode ne nécessite que la compilation de constants.cpp (et la liaison, bien sûr).

Comme dans la plupart des cas, il s’agit ou non de votre cas, mais c’est une autre option à envisager.

2
aldo

La méthode simple consiste à créer des symboles non const:

const bool doX = true;
const bool doY = false;
const int maxNumX = 5;

Ces valeurs seront remplacées par le compilateur avec les valeurs données. C'est le moyen le plus efficace. Bien entendu, cela entraîne également une recompilation dès que vous modifiez ou ajoutez des valeurs. Mais dans la plupart des cas, cela ne devrait pas poser de problèmes pratiques.

Bien sûr, il existe différentes solutions:

  • À l'aide de static consts (ou de membres statiques de la classe const), les valeurs peuvent être modifiées sans recompilation de tous les fichiers référencés. Toutefois, les valeurs sont conservées dans un segment de données const qui sera appelé lors de l'exécution plutôt que d'être résolu à la compilation. Si les performances d'exécution ne sont pas un problème (comme c'est le cas pour 90% du code le plus typique), c'est correct.

  • La méthode C++ directe utilise la classe enums plutôt que les identifiants const globaux (comme indiqué par Mathieu). Ceci est plus typé et à part cela fonctionne beaucoup plus comme const: Les symboles seront résolus au moment de la compilation.

0
RED SOFT ADAIR