Il y a trop de réponses différentes sur le débordement de pile:
Déclarez un espace de noms, et dans le fichier hpp, marquez toutes les chaînes comme extern const, et dans le fichier cpp, mettez leurs définitions.
Utilisez static const au lieu de extern const:
Utilisez une fonction en ligne:
constantes de chaîne statiques dans la classe vs espace de noms pour les constantes [c ++]
Utilisez des espaces de noms anonymes!
Tout cela est vraiment déroutant. Utiliser une fonction en ligne semble être un moyen très long et fastidieux de renvoyer une constante chaîne.
Pourquoi ne pouvons-nous pas simplement utiliser des espaces de noms contenant des chaînes statiques, qui sont définies dans le fichier cpp?
Quelqu'un peut-il fournir une réponse catégorique / sans équivoque sur la manière de stocker des chaînes dans un fichier constant, afin que plusieurs fichiers cpp puissent y accéder?
MODIFIER: En C # et Java, il ne semble pas être un problème de stocker toutes vos constantes dans un fichier "Constantes". Quelle est la manière la plus simple de faire cela en C ++?
EDIT: Ces réponses en Java semblent spécifiques et compréhensibles. En C ++, il est difficile de savoir quelle est la manière susceptible d'avoir le moins de temps de compilation, le moins d'utilisation de mémoire au moment de l'exécution.
Partager des chaînes constantes en Java entre de nombreuses classes?
https://dzone.com/articles/constants -en-java-le-anti-motif-1
4 Réponses :
Vous devez les définir dans un fichier C ++ (sinon vous obtiendrez plusieurs erreurs de définitions):
strings_id.cpp
#include "strings_id.h" printf(TXT_TEST1);
Et vous devez les déclarer dans un fichier d'en-tête qui sera inclus là où vous en avez besoin:
strings_id.h
#include "strings_id.h" printf(TXT_TEST1);
Utilisation de l'espace de noms recommandée.
class1.cpp
extern const char* TXT_TEST1;
class2.cpp p >
const char* TXT_TEST1 = "Test1";
Il semble que personne ne connaisse vraiment la réponse, puisque certaines personnes utilisent char * mais d'autres disent de n'utiliser que std :: string. Certains utilisent une chaîne statique, d'autres utilisent extern. Pourquoi n'utilisez-vous donc pas un espace de noms dans votre réponse?
N'avez-vous pas mélangé les deux premiers fichiers? Vous déclarez dans le .cpp et définissez dans le .h. Aussi, suggérez-vous d'utiliser .hpp
pour distinguer les en-têtes C.
@KaizerSozay Aucun espace de noms n'est utilisé car la réponse ne change pas après l'avoir fait, et vous seul savez quel groupement d'espaces de noms convient le mieux à votre cas. C'est la même chose qu'une réponse disant "choisissez de meilleurs noms" (parce que le contexte compte) et vous demandez "pourquoi n'avez-vous pas choisi de meilleurs noms" - ce n'est pas le but de la réponse et ne fait rien pour le clarifier. Il n'y a pas non plus de gardes d'inclusion ici, même si vous devriez certainement les avoir.
@KaizerSozay 1) Les espaces de noms ne changent pas le problème. 2) std :: string
n'est pas recommandé pour l'exemple que j'ai fourni, il ne crée qu'une instance de plus. Peut être utilisé, mais vous n'avez tout simplement pas besoin d'une chaîne std :: string ici. 3) Qui dit utiliser l'électricité statique a tort. static signifie que le var sera disponible dans la même unité de stranlation. Si vous utilisez static
, vous ne pouvez pas utiliser la variable de h dans un autre fichier cpp
alors pourquoi préférez-vous extern à constexpr?
@KaizerSozay Vous voulez dire constexpr
à const
?
La réponse d'AImx1 est similaire à la vôtre, mais au lieu d'utiliser extern const, il utilise constexpr const.
extern
indique au compilateur que la variable est déclarée dans un autre module. const
et plus récent constexpr
indique que la variable ne peut pas être modifiée. En effet, les chaînes littérales sont stockées dans la section de données et toute tentative de les modifier entraînera une violation d'accès.
constexpr est const en c ++ 11. Si vous n'avez pas de problème de portabilité, vous pouvez utiliser constexpr
. Il n'y a aucune différence dans le programme généré. Edit2: constexpr const
ne vous permet pas d'attribuer une valeur à cette variable. C'est bon de l'utiliser
Vous ne pouvez pas simplement les définir dans un fichier .cpp car vous voulez que leur déclaration soit visible dans d'autres fichiers d'en-tête et cpp. Vous n'incluez pas de fichiers .cpp, donc le compilateur ne saura pas comment résoudre les noms de vos chaînes au moment de la compilation. Il compilera vos définitions séparément dans un fichier objet qui correspond au fichier cpp où vous définissez les chaînes, mais il ne pourra pas compiler les fichiers qui utilisent les chaînes, et donc lier le programme tous ensemble. Vous devriez utiliser un moyen de déclarer vos chaînes (ou un moyen d'accéder à ces chaînes, une fonction d'encapsulation en ligne par exemple) dans un fichier d'en-tête.
Vous ne pouvez pas simplement les définir sans mot-clé supplémentaire dans le fichier d'en-tête, car cela les créera dans chaque fichier objet qui sera compilé à partir de .cpp qui comprend cet en-tête.
Vous devriez donc utiliser un mot-clé de classe de stockage qui définira les chaînes globalement. Vous pouvez lire la différence entre static
et extern
dans cette question .
L'espace de noms anonyme rendra votre chaîne visible uniquement dans l'unité de traduction (fichier .h ou .cpp) où vous les définissez. Ce n'est donc pas ce que vous voulez.
Notez également qu'une fonction en ligne est implicitement externe.
< gagnantEdit:
Personnellement, j'utiliserais uniquement le mot-clé extern
car je ne vois aucun intérêt à utiliser les fonctions d'encapsulation en ligne. Mais vous pouvez toujours les utiliser en combinaison avec static
pour la définition des constantes où vous définissez votre fonction wrapper. Toujours utiliser uniquement le mot-clé static
dans la définition des constats rendra les constantes accessibles uniquement à partir du fichier où vous les définissez.
Merci pour votre réponse, mais comme il y a tellement de façons de faire les choses, je ne sais toujours pas comment résoudre le problème.
@KaizerSozay Pourquoi ne pas choisir une solution, au lieu de s'inquiéter du fait qu'il existe plusieurs solutions.
@john Pour être honnête John, il y a tellement de réponses différentes sur donc pourquoi faire / pourquoi ne pas faire, ce n'est vraiment pas clair. Pour la plupart des autres choses, je suis convaincu que mon application n'aura aucun problème après sa sortie, mais comme cela n'est incroyablement pas clair, plutôt que de «choisir un» et de risquer que mon application ait une version terrible, je préfère passer un peu de temps à comprendre quelle option est mieux, et pourquoi.
@KaizerSozay a ajouté la façon dont je préférerais personnellement dans l'édition.
Pouvez-vous publier un exemple de code s'il vous plaît?
@KaizerSozay cprogrammer a publié un exemple de code dans sa réponse, vous pouvez également en trouver dans les liens que vous avez fournis.
@KaizerSozay Certains des problèmes qui vous préoccupent sont des problèmes de style, il n'y a donc pas de meilleure solution claire. Par exemple std :: string
vs char *
et s'il faut utiliser un espace de noms.
@john Ok. Merci! Mais ce qui m'inquiète, c'est s'il existe plusieurs copies de constantes au moment de l'exécution.
Partager des chaînes constantes en Java entre de nombreuses classes?
Si vous voulez quelque chose de similaire, vous pouvez créer un fichier d'en-tête
#ifndef EVENT_NAME_LITERALS_HXX_ #define EVENT_NAME_LITERALS_HXX_ namespace eventnames { constexpr const char* EVENT1 = "event1"; constexpr const char* EVENT2 = "event2"; } #endifAppelons-le
test.hpp
p>Vous pouvez définir les constantes que vous souhaitez dans ce fichier d'en-tête et les réutiliser dans tout fichier CPP que vous souhaitez utiliser.
La question ci-dessous fournit plus d'informations:
utilisation de constexpr dans le fichier d'en-tête
constexpr implique const et const sur la portée globale / espace de noms implique statique (liaison interne), ce qui signifie que chaque unité de traduction y compris cet en-tête obtient sa propre copie de EVENT1 et EVENT2. La mémoire pour ça statique ne sera alloué que si une adresse ou une référence à celle-ci est prise, et l'adresse sera différente dans chaque traduction unité.
Cela implicite statique pour les variables const a été introduit spécifiquement pour utilisez const au lieu de #define dans les fichiers d'en-tête en C ++ pour définir constantes. Sans statique, il y aurait plusieurs définitions de symboles erreur de l'éditeur de liens si ce fichier d'en-tête est inclus dans plus d'un unité de traduction qui étaient liées ensemble.
Si vous ne voulez pas avoir de copies des constantes dans chaque unité de traduction qui utilise ce fichier d'en-tête, cependant, l'éditeur de liens sera suffisamment intelligent pour optimiser les copies multiples. Vous pouvez également "extern" comme spécifié dans d'autres réponses.
#define _TEST_HXX_
Cet identifiant est réservé. En le définissant, votre programme aura un comportement indéfini. Vous devez choisir un autre garde d'en-tête.
Pourquoi n'avez-vous pas utilisé un espace de noms, et au lieu de char * pourquoi n'avez-vous pas utilisé std :: string? Aussi pourquoi n'avez-vous pas utilisé extern dans le fichier d'en-tête et spécifié la valeur des chaînes dans le fichier cpp?
@KaizerSozay Il ne sert à rien d'utiliser des espaces de noms anonymes dans le fichier d'en-tête. Je n'ai pas utilisé std :: string car ils sont coûteux et dans ce cas vos noms d'événements ne changent pas, alors pourquoi ne peuvent-ils pas être des chaînes littérales.
@eerorika Merci pour l'indice, j'ai mis à jour mon message
@ AImx1 Je ne parlais pas d'espace de noms anonyme. Je veux dire pourquoi n'avez-vous pas utilisé un espace de noms comme "EventNames" ou quelque chose comme ça, afin que nous puissions regrouper les constantes liées?
@ AImx1 _CONST_STRING_LITERALS_HXX_
l'identifiant est également réservé.
@KaizerSozay bien sûr, vous pouvez utiliser tout ce que vous voulez. Je veux juste vous donner une idée de comment l'utiliser. Quoi qu'il en soit, je mettrai à jour le message.
@ AImx1Si c'est aussi simple, pouvez-vous expliquer pourquoi il y a tant de réponses différentes sur SO? Il semble que cette question n'aurait dû être posée qu'une seule fois et qu'il devrait y avoir une réponse claire.
@ AImx1 _EVENT_NAME_LITERALS_HXX_
est également un identifiant réservé.
@KaizerSozay Tout dépend du contexte dans lequel vous essayez de résoudre le problème. La solution qui est idéale dans un contexte pourrait ne pas être idéale dans un autre contexte. Alors choisissez votre contexte et réfléchissez à la solution.
@eerorika veuillez suggérer un identifiant, je suis à court d'idées.
Selon cette question: stackoverflow.com/questions/3684450/... peut-être statique est la mauvaise option? Parce qu'il semble que statique ne doit être utilisé que localement dans un fichier, mais extern doit être utilisé pour les variables globales
@ AImx1 par exemple EVENT_NAME_LITERALS_HXX_
. Je recommande de consulter les règles linguistiques concernant les identifiants réservés.
Cette réponse: stackoverflow.com/questions/34578993/... semble avoir le plus de sens. Mais je ne comprends pas ce qu'il veut dire à propos de l'utilisation d'objets statiques?
pourquoi n'avez-vous pas utilisé extern alors? Ne voulez-vous pas avoir des copies de constantes dans différentes unités de traduction?
constexpr
implique const
d'ailleurs, donc le dernier const
dans chaque déclaration est inutile.
Cette question est beaucoup trop large pour recevoir une réponse. Mais sur la base de précisions supplémentaires concernant la nature de l'application (noms utilisés en interne pour enregistrer les événements) données dans les commentaires, je suggérerais probablement quelque chose du genre à utiliser un en-tête comme celui-ci
#ifndef INCLUDED_EVENT_NAMES #define INCLUDED_EVENT_NAMES #pragma once namespace event_names { constexpr auto& name1 = "value1"; constexpr auto& name2 = "value2"; } #endif
Utilisation de std :: string_view constantes plutôt que des constantes const char *
ou const char [N]
signifie que vous connaissez la longueur de chaque chaîne et que vous ne le faites pas doivent compter sur une résiliation nulle. L'utilisation de std :: string
entraînerait presque certainement une surcharge de mémoire et des coûts d'initialisation au moment de l'exécution. Les std :: string_view
définis ici seront compilés en code faisant directement référence aux objets littéraux de chaîne alloués statiquement. Même lorsque la même constante est utilisée dans plusieurs unités de traduction (fichiers .cpp), les compilateurs modernes ne stockeront presque certainement le même littéral de chaîne dans le binaire qu'une seule fois (une optimisation standard activée par [lex.string] / 15 )
Si vous êtes bloqué avec C ++ 11, alors c'est le plus simple (et probablement suffisant pour ce que vous devez faire) de simplement créer des références nommées pour les objets littéraux de chaîne dont vous avez besoin:
#ifndef INCLUDED_EVENT_NAMES #define INCLUDED_EVENT_NAMES #pragma once #include <string_view> namespace event_names { using namespace std::literals; inline constexpr auto name1 = "value1"sv; inline constexpr auto name2 = "value2"sv; } #endif
Puisque les références ne sont pas des objets, c'est impossible pour quiconque de faire quoi que ce soit (accidentellement ou autrement) qui provoquerait la création d'un objet réel pour la constante elle-même (sauf pour l'objet littéral de chaîne bien sûr). De plus, comme il s'agit d'une référence, il contiendra les informations sur la taille du tableau, au cas où quelque chose pourrait en tirer parti (sachez que cette taille comprend le null de fin). Et comme il y a encore la conversion implicite en const char *
, vous pourrez les utiliser partout où une ancienne chaîne C est requise.
std :: string_view
est là depuis C ++ 17. La question est balisée avec C ++ 11. (Je suggérerais de considérer les variables en ligne autrement.)
Pourquoi n'avez-vous pas utilisé extern? Cela n'entraînerait-il pas plusieurs copies de constantes, ce qui n'est pas vraiment utile?
@KaizerSozay Avez-vous lu la réponse? Cela explique que les éditeurs de liens ont tendance à être suffisamment intelligents pour fusionner les copies en une seule pour le binaire final. Et avoir les définitions visibles dans chaque cpp peut permettre des optimisations (augmentant ainsi la vitesse d'exécution, mais éventuellement ralentissant la compilation). Tout est un compromis. Mais honnêtement, combien de chaînes (et leurs utilisations) votre application implique-t-elle qu'il vaut la peine de passer des heures à trouver le concept de stockage le plus efficace pour elles? Tout cela me semble être une optimisation prématurée.
@MaxLanghof Ce n'est certainement pas le même que certainement
@KaizerSozay Et pourtant, je suis presque certain que rien de tout cela n'a d'importance. Ajoutez votre modèle d'utilisation à la question et nous pouvons vous dire ce qui serait probablement le mieux pour votre cas particulier, que vous pourrez ensuite confirmer. Si vous ne le faites pas, vous n’obtiendrez pas plus que presque certain . Pour commencer, nous ne connaissons ni votre compilateur ni votre plateforme, ni le nombre de chaînes (et leur fréquence d'utilisation). Tout cela est important si vous voulez des réponses plus précises.
@KaizerSozay De plus, vous devez soit faire confiance à votre compilateur (vous devez le faire au moins en partie de toute façon), soit vérifier ce qu'il fait (ce que vous prétendez ne pas vouloir faire parce que vous ne vous sentez pas assez compétent ). Si vous faites confiance à votre compilateur, alors les réponses stackoverflow ne pourront pas en dire plus que " probablement ", car les compilateurs changent. Et nous ne pouvons pas vérifier votre compilateur exact si vous ne nous dites pas lequel vous utilisez (non pas que ce soit une promesse absolue que quiconque le ferait).
@DanielLangr J'ai en effet oublié la balise C ++ 11, merci de l'avoir signalé.
@KaizerSozay juste pour confirmer, êtes-vous effectivement limité au C ++ 11?
@MichaelKenzel Oui. Nous sommes coincés avec C ++ 11
@KaizerSozay J'ai mis à jour ma réponse pour tenir compte de cela.
@MichaelKenzel Puisque vous avez mentionné qu'il pourrait être plus facile de donner une réponse concrète à une question concrète, j'ai créé ceci: stackoverflow.com/questions/55498738/...
Juste pour clarifier: vous voulez avoir un fichier
.hpp
, qui sera inclus dans plusieurs unités de traduction, leur permettra à tous d'utiliser des constantes de chaîne, mais les stockera une seule fois dans le binaire lié. Ai-je raison?Malheureusement, il n'y a pas de manière «catégorique / sans équivoque», il n'y a pas une seule «bonne» ou «bonne» façon de faire cela. Cela rend cette question subjective.
@Someprogrammerdude Quel moyen aurait le moins de temps de compilation, et utiliserait le moins de mémoire et de temps processeur, avec le moins de risques d'erreurs alors?
Quel est exactement le but "d'accéder aux constantes de chaîne à partir de plusieurs classes"? Pourquoi plusieurs classes dépendent-elles des mêmes constantes en premier lieu? Comment prévoyez-vous exactement "accéder" aux constantes de chaîne? Via un nom? Via un index? Est-ce pour mettre en œuvre l'internationalisation? De quel type de chaîne parlons-nous?
const char [N]
?std :: string
? autre chose? Cette question est trop vague et générale car une réponse utile pourrait être donnée ...@MichaelKenzel Ce n'est pas pour l'internationalisation. C'est pour stocker les noms d'événements. N'importe quelle classe peut s'inscrire à des événements spécifiques, et on ne sait pas à l'avance quelle classe aimerait s'inscrire pour quel événement. C'est pourquoi plusieurs classes doivent accéder au fichier.
Pourquoi les noms d'événements doivent-ils être des chaînes? Ne pourriez-vous pas simplement utiliser, par exemple, une énumération pour cela !?
@KaizerSozay Dans ce cas, l'utilisation d'un fichier d'en-tête avec statique const char * vous aidera.
@MichaelKenzel C'est l'exigence, et malheureusement je ne suis pas en mesure de discuter de la raison pour laquelle ce devrait être une chaîne.
C'est assez juste. Je vous propose d'éditer votre question pour en faire une question concrète sur votre problème concret afin qu'une réponse concrète puisse être donnée.
@MichaelKenzel Je recherche une réponse similaire à celle de cette question: stackoverflow.com/questions/10896111/...
"Le moins de temps de compilation, et utilise le moins de temps de mémoire et de processeur, avec le moins de risque d'erreur" Vous ne pouvez jamais obtenir les quatre, si vous avez de la chance, vous pouvez en choisir deux.
@Someprogrammerdude Y a-t-il une réponse que vous suggéreriez alors?
@KaizerSozay Vous pouvez effectuer le benchmark sur ces 4 implémentations différentes et pouvez le découvrir? En dehors de cela, à ma connaissance, l'utilisation de littéraux de chaîne "static const char * const" devrait être rapide pendant l'exécution car tout sera en lecture seule du binaire.
@KaizerSozay Je pense que ce que les autres ont essayé de dire, c'est qu'il n'y a pas de bonne réponse . C'est assez courant avec C ++ que différentes approches ont des avantages (et des inconvénients) différents et vous ne pouvez pas tous les avoir. Votre question est trop large pour fournir des conseils génériques. Si vous le modifiez pour vous concentrer sur votre problème particulier, vous obtiendrez probablement de meilleures réponses.
@KaizerSozay En étant plus précis, je voulais décrire votre vrai problème que vous résolviez (vous avez fourni plus de détails dans les commentaires, mais éditer la question serait mieux). Au mieux, vous devriez fournir un exemple fonctionnel, tel qu'un code (minimal) de, par exemple, 2 fichiers source qui doivent partager ces constantes de chaîne avec un moyen de leur utilisation.