J'ai vu les utilisations de ces deux expressions: portée globale et espace de noms global. Quelle est la différence entre eux?
En C++, chaque nom a sa portée en dehors de laquelle il n'existe pas. Une portée peut être définie de plusieurs façons: elle peut être définie par namespace, fonctions, classes et juste {} =.
Un espace de noms, global ou autre, définit donc une portée. L'espace de noms global fait référence à l'utilisation de ::
, et les symboles définis dans cet espace de noms auraient une portée globale. Un symbole, par défaut, existe dans un espace de noms global, à moins qu'il ne soit défini à l'intérieur d'un bloc commence par le mot clé namespace
, ou qu'il soit membre d'une classe, ou une variable locale d'une fonction:
int a; //this a is defined in global namespace
//which means, its scope is global. It exists everywhere.
namespace N
{
int a; //it is defined in a non-global namespace called `N`
//outside N it doesn't exist.
}
void f()
{
int a; //its scope is the function itself.
//outside the function, a doesn't exist.
{
int a; //the curly braces defines this a's scope!
}
}
class A
{
int a; //its scope is the class itself.
//outside A, it doesn't exist.
};
Notez également qu'un nom peut être masqué par la portée interne définie par l'espace de noms, la fonction ou la classe. Ainsi, le nom a
à l'intérieur de l'espace de noms N
cache le nom a
dans l'espace de nommage global. De la même manière, le nom dans la fonction et la classe masque le nom dans l'espace de noms global. Si vous êtes confronté à une telle situation, vous pouvez utiliser ::a
pour faire référence au nom défini dans l'espace de noms global:
int a = 10;
namespace N
{
int a = 100;
void f()
{
int a = 1000;
std::cout << a << std::endl; //prints 1000
std::cout << N::a << std::endl; //prints 100
std::cout << ::a << std::endl; //prints 10
}
}
"Scope" est un terme plus général que "namespace". Chaque espace de noms, classe et bloc de code définit une portée dans laquelle les noms déclarés à l'intérieur peuvent être utilisés; un espace de noms est un conteneur pour les noms déclarés en dehors des classes et des fonctions.
"Portée globale" et "espace de noms global" peuvent être utilisés de manière plus ou moins interchangeable; la portée d'un nom déclaré dans un espace de noms couvre l'ensemble de cet espace de noms. Utilisez "namespace" si vous faites spécifiquement référence à l'espace de noms, et "scope" si vous faites référence à la visibilité des noms à l'intérieur.
La portée indique la durée de vie d'un objet, vous pouvez avoir une variable globale qui existera aussi longtemps que votre programme s'exécutera, ou vous pouvez avoir une variable avec une portée de bloc qui existera aussi longtemps que ce bloc de code s'exécutera. Considérez cet exemple:
#include <iostream>
int a = 100;
main () {
int a = 200;
std::cout << "local a is: " << a << std::endl;
std::cout << "global a is: " << ::a << std::endl;
return 0;
}
Une fois exécutée, l'instruction affichera local a is: 200
, ce qui est évidemment attendu, car nous redéfinissons a
dans main
, ce qui laisse dans la portée de son bloc englobant. Nous imprimons également le global ::a
qui imprime à nouveau la valeur attendue 100, car nous avons demandé l'espace de noms global ::
.
La sémantique d'un espace de noms est principalement logique, c'est un moyen d'isoler les symblos les uns des autres, dans l'espoir d'éviter les conflits de noms, cela n'affecte pas la durée de vie d'un objet.
La portée d'autre part, dénote la durée de vie d'un objet, le a
global a vu le jour avant le a
local parce qu'il est construit bien plus tôt que le principal n'est exécuté. Cependant, la portée aussi force un espace de noms sur le symbole, mais pas de la même manière qu'un namespace
. Il existe différents types de portées, global
, class
, function
, block
, file
, etc.
La partie déroutante est que la portée est parfois surchargée pour dénoter la visibilité d'un symbole particulier, qui est quelque chose emprunté à C, où la notion d'espaces de noms n'existait pas et la portée était utilisée pour désigner à la fois la durée de vie et la visibilité. En C++, cependant, les règles ont un peu changé, mais le terme scope est toujours utilisé de la même manière car les deux langages partagent beaucoup de concepts.
Lorsque vous déclarez une variable globale int i
par exemple, nous disons i is in the global namespace
et has the global namespace scope
. C'est tout.
Extrait de C++ 03:
3.3.5 Namespace scope
The outermost declarative region of a translation unit is also a namespace, called
the global namespace. A name declared in the global namespace has global namespace
scope (also called global scope).
@Dmitriy Ryajov
Le sujet est un peu ancien mais je veux offrir mon aide à ce sujet. Je pense que vous ne devriez pas rendre les choses plus compliquées qu'elles ne le sont vraiment. Scope
d'un identifiant est la partie d'un programme informatique où l'identifiant, un nom qui fait référence à une entité dans le programme, peut être utilisé pour trouver l'entité référencée. Le terme scope ne s'applique donc qu'aux identifiants et nous ne devons pas le mélanger avec la durée de vie de l'objet. Ils sont quelque peu liés mais ne doivent pas être mélangés. La durée de vie de l'objet est indiquée par l'endroit où nous allouons la mémoire pour cet objet. Ainsi, par exemple, si une mémoire est allouée sur la pile, elle sera libérée dès la fin de la fonction. Cela dépend donc de l'endroit où nous stockons l'objet et cela dénote sa durée de vie. La portée indique seulement: "Voici un nom pour un objet et nous pouvons utiliser ce nom pour l'objet jusque-là et ensuite". Donc, comme je l'ai dit, le terme scope
est uniquement pour les identifiants des objets et la durée de vie est autre chose qui est indiquée par l'endroit où nous stockons l'objet.
De plus, je veux dire quelque chose sur linkage
qui est étroitement lié à cela. Cela peut aussi parfois prêter à confusion. Disons que nous avons des identifiants dans le translation unit
qui font référence à certains objets. Le fait que les mêmes identificateurs dans l'unité de traduction other
se réfèrent aux mêmes entités est indiqué par la liaison. Ainsi, par exemple, si un identifiant a un lien externe, nous pouvons faire référence à l'entité à laquelle cet identifiant fait référence, mais à partir d'une autre unité de traduction en le déclarant avec le mot clé extern
. Maintenant, disons que nous ne voulons pas utiliser cette entité dans d'autres unités de traduction. Ensuite, l'entité exist
jusqu'à la fin du programme mais lorsque nous ne le déclarons pas, nous ne pourrons pas nous y référer. Notez également que maintenant j'ai mélangé les termes lien et durée de vie. Mais c'est parce que seules les entités global
ont une liaison externe. Un identifiant à l'intérieur d'une fonction ne peut pas être référencé à partir des autres parties du programme.
Conclusion: Essayez toujours de garder les choses simples. J'ai été surpris de voir comment différentes personnes parlent différemment de ces termes. L'ensemble du processus de compilation séparée est déroutant, car il existe plusieurs termes qui ont presque la même signification et probablement tout le monde sera bloqué à ce stade.