Je viens de rejoindre un nouveau projet logiciel C++ et j'essaie de comprendre le design. 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 pouvant amener un utilisateur à utiliser un espace de nom non nommé? Quels sont les avantages et les inconvénients?
(Dans la suite, les choses ne s'appliquent plus à C++ 11, mais s'appliquaient à C++ 03. C++ 11 ne fait presque plus de différence (s'il y en a, ce sont juste des différences d'avocat de langue qui Je ne me souviens pas).
Les espaces de noms non nommés sont un utilitaire permettant de créer un identificateur de traduction d'identificateur local. Ils se comportent comme si vous choisissiez 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, vous pouvez donc déjà faire référence à des identifiants comme ::name
qui sont définis dans cet espace de noms, car la directive using a déjà eu lieu.
Cela signifie que vous pouvez avoir des fonctions gratuites appelées (par exemple) help
qui peuvent exister dans plusieurs unités de traduction et qui ne seront pas en conflit au moment de la liaison. L'effet est presque identique à l'utilisation du mot clé static
utilisé en C que vous pouvez insérer dans la déclaration des identificateurs. Les espaces de noms non nommés sont une alternative supérieure, pouvant même créer une unité de traduction de type locale.
namespace { int a1; }
static int a2;
Les deux a
sont des unités de traduction locales et n'entrent pas en conflit au moment de la liaison. Mais la différence est que le a1
dans l'espace de noms anonyme obtient un nom unique.
Lisez l'excellent article sur comeau-computing Pourquoi un espace de nom non nommé est-il utilisé au lieu de statique? ( Archive.org miroir ).
Avoir quelque chose dans un espace de noms anonyme signifie qu'il est local par ceci nité de traduction (fichier .cpp et tous ses inclus), cela signifie que si un autre symbole portant le même nom est défini ailleurs, il n'y aura pas de violation de la ne règle de définition (ODR).
C'est la même chose que la manière C d'avoir une variable globale statique ou une fonction statique, mais cela peut aussi être utilisé pour les définitions de classe (et devrait être utilisé plutôt que static
en C++).
Tous les espaces de noms anonymes dans le même fichier sont traités comme un même espace de noms et tous les espaces de noms anonymes dans des fichiers différents sont distincts. Un espace de noms anonyme est l'équivalent de:
namespace __unique_compiler_generated_identifer0x42 {
...
}
using namespace __unique_compiler_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;
Ceux-ci n'ont pas besoin d'être dans un espace de noms anonyme, car l'objet const
a déjà une liaison statique et ne peut donc pas être 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 (dans la norme, le libellé suggère que les statiques de fichiers sont en quelque sorte déconseillées au profit d'espaces de noms anonymes), mais si vous travaillez dans un grand projet C++ comme KDE, vous obtenez beaucoup de gens qui tournent la tête dans la bonne direction. encore une fois :)
Outre les autres réponses à cette question, l'utilisation d'un espace de noms anonyme peut également améliorer les performances. Comme les symboles au sein de l'espace de noms n'ont pas besoin de liaison 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 une fois dans une boucle peut être inséré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 de add_val empêche le compilateur de l'inclure. deux fois).
#include <iostream>
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. incluses uniquement à l'intérieur de ce fichier. Dans votre exemple, c'est un moyen d'éviter les variables globales. Il n'y a pas de différence de performances d'exécution ou de compilation.
Il n'y a pas tellement d'avantage ou de désavantage en dehors de "Est-ce que je veux que cette variable, fonction, classe, etc. soit publique ou privée?"
L'espace de nom sans nom limite l'accès de la classe, de la variable, de la fonction et des objets au fichier dans lequel il est défini. La fonctionnalité d'espace de nom sans nom est similaire au mot clé static
en C/C++.static
mot clé limite l'accès de la variable globale et de la fonction au fichier dans lequel elles sont définies.
Il existe une différence entre les espaces de noms non nommés et le mot clé static
en raison de l’espace de noms non nommé qui présente un avantage par rapport à statique. Le mot clé static
peut être utilisé avec une variable, une fonction et des objets, mais pas avec une 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 pareil peut être possible avec un espace de noms sans nom. Par exemple,
namespace {
class xyz {/*Body of class*/}
static structure {/*Body of structure*/}
} //Correct