Je viens de rejoindre un nouveau projet de logiciel C ++ et j’essaie de comprendre la conception. Le projet utilise fréquemment des espaces de noms non nommés. Par exemple, quelque chose comme ceci peut se produire dans un fichier de définition de classe:
// newusertype.cc namespace { const int SIZE_OF_ARRAY_X; const int SIZE_OF_ARRAY_Y; bool getState(userType*,otherUserType*); } newusertype::newusertype(...) {...
Quelles sont les considérations de conception susceptibles de provoquer l’utilisation d’un espace de noms sans nom? Quels sont les avantages et les inconvénients?
(Dans ce qui suit, les choses nestedes sont des choses qui ne s’appliquent plus à C ++ 11, mais s’appliquent à C ++ 03. C ++ 11 ne fait presque plus de différences (s’il y en a, elles ne sont que du langage) différences d’avocat que je ne me souviens pas).).
Les espaces de nommage sans nom sont un utilitaire permettant de rendre un identifiant local de traduction efficace . Ils se comportent comme si vous choisiriez un nom unique par unité de traduction pour un espace de noms:
namespace unique { /* empty */ } using namespace unique; namespace unique { /* namespace body. stuff in here */ }
L’étape supplémentaire utilisant le corps vide est importante, de sorte que vous pouvez déjà faire référence dans le corps de l’espace de nommage aux identificateurs tels que ::name
définis dans cet espace de noms, car la directive using a déjà été utilisée.
Cela signifie que vous pouvez avoir des fonctions gratuites appelées (par exemple) une help
pouvant exister dans plusieurs unités de traduction, et qu’elles ne se heurteront pas au moment de la liaison, car elles ont toutes un nom unique en raison de leur espace de nom unique . L’effet est presque identique à l’utilisation du mot-clé static
utilisé dans C que vous pouvez insérer dans la déclaration des identificateurs. espaces de noms non nommés sont une alternative supérieure, pouvant même rendre une unité de traduction de type locale. static
utilisé de cette manière est déconseillé en C ++, car les
namespace { int a1; } static int a2;
Les deux a
sont une unité de traduction locale et ne se heurteront pas au moment du lien. Mais la différence est que le a1
dans l’espace de noms anonyme ne reçoit qu’un nom unique. Il a encore un lien externe et peut être exporté dans la table des symboles du fichier object en cours de création. Cela devient important si vous souhaitez utiliser son adresse comme argument de modèle:
template struct sample { }; // OK - a1 has external linkage sample<&a1> s1; // NOT OK - translation unit locality is done by giving a2 internal linkage. sample<&a2> s2;
Les parameters de modèle doivent avoir une liaison externe, de sorte que dans ce cas, l’identifiant doit être placé dans un espace de noms anonyme.
Lisez l’excellent article de comeau-computing `Pourquoi un espace de nom sans nom est-il utilisé à la place du statique? .
Avoir quelque chose dans un espace de noms anonyme signifie qu’il est local pour cette unité de traduction (fichier .cpp et toutes ses inclusions) cela signifie que si un autre symbole du même nom est défini ailleurs, il n’y aura pas violation de la règle de définition .
C’est la même chose que C pour avoir une variable globale statique ou une fonction statique, mais elle peut aussi être utilisée pour les définitions de classes (et devrait être utilisée plutôt que static
en C ++).
Tous les espaces de noms anonymes du même fichier sont traités comme le même espace de noms et tous les espaces de noms anonymes des différents fichiers sont distincts. Un espace de noms anonyme équivaut à:
namespace __unique_comstackr_generated_identifer0x42 { ... } using namespace __unique_comstackr_generated_identifer0x42;
L’exemple montre que les personnes du projet que vous avez rejoint ne comprennent pas les espaces de noms anonymes 🙂
namespace { const int SIZE_OF_ARRAY_X; const int SIZE_OF_ARRAY_Y;
Celles-ci n’ont pas besoin d’être dans un espace de noms anonyme, car les objects const
ont déjà une liaison statique et ne peuvent donc pas entrer en conflit avec les identifiants du même nom dans une autre unité de traduction.
bool getState(userType*,otherUserType*); }
Et c’est en fait une pessimisation: getState()
a un lien externe. Il est généralement préférable de préférer le couplage statique, car cela ne pollue pas la table des symboles. Il vaut mieux écrire
static bool getState(/*...*/);
ici. Je suis tombé dans le même piège (il y a un libellé dans la norme qui suggère que les statiques de fichiers sont en quelque sorte déconseillées en faveur d’espaces de noms anonymes), mais dans un grand projet en C ++ comme KDE, beaucoup de personnes tournent la tête à nouveau 🙂
Outre les autres réponses à cette question, l’utilisation d’un espace de noms anonyme peut également améliorer les performances. Comme les symboles dans l’espace de noms n’ont besoin d’aucun lien externe, le compilateur est plus libre d’effectuer une optimisation agressive du code dans l’espace de noms. Par exemple, une fonction appelée plusieurs fois dans une boucle peut être intégrée sans impact sur la taille du code.
Par exemple, sur mon système, le code suivant prend environ 70% du temps d’exécution si l’espace de noms anonyme est utilisé (x86-64 gcc-4.6.3 et -O2; notez que le code supplémentaire dans add_val ne permet pas au compilateur d’inclure deux fois).
#include namespace { double a; void b(double x) { a -= x; } void add_val(double x) { a += x; if(x==0.01) b(0); if(x==0.02) b(0.6); if(x==0.03) b(-0.1); if(x==0.04) b(0.4); } } int main() { a = 0; for(int i=0; i<1000000000; ++i) { add_val(i*1e-10); } std::cout << a << '\n'; return 0; }
Un espace de noms anonyme rend les variables, fonctions, classes, etc. disponibles uniquement dans ce fichier. Dans votre exemple, c’est un moyen d’éviter les variables globales. Il n’y a pas de différence de performances à l’exécution ou à la compilation.
Mis à part “est-ce que je veux que cette variable, fonction, classe, etc. soit publique ou privée?”
Un espace de nommage sans nom limite l’access de la classe, de la variable, de la fonction et des objects au fichier dans lequel il est défini. La fonctionnalité d’espace de nommage sans nom est similaire au mot-clé static
en C / C ++.
static
mot-clé static
limite l’access de la variable globale et de la fonction au fichier dans lequel elles sont définies.
Il y a une différence entre les espaces de noms non nommés et static
mots-clés static
car les espaces-noms non nommés ont un avantage sur les statiques. static
mot-clé static
peut être utilisé avec variable, function et objects mais pas avec la classe définie par l’utilisateur.
Par exemple:
static int x; // Correct
Mais,
static class xyz {/*Body of class*/} //Wrong static structure {/*Body of structure*/} //Wrong
Mais la même chose peut être possible avec un espace de nom sans nom. Par exemple,
namespace { class xyz {/*Body of class*/} static structure {/*Body of structure*/} } //Correct