【发布时间】:2024-01-24 12:24:01
【问题描述】:
我看到了这两个短语的用法:全局作用域和全局命名空间。它们有什么区别?
【问题讨论】:
标签: c++ namespaces scope terminology
我看到了这两个短语的用法:全局作用域和全局命名空间。它们有什么区别?
【问题讨论】:
标签: c++ namespaces scope terminology
在 C++ 中,每个名称都有其不存在的范围。范围可以通过多种方式定义:它可以通过 namespace、functions、classes 和 { }.
所以一个命名空间,无论是全局的还是其他的,都定义了一个范围。全局命名空间是指使用::,在这个命名空间中定义的符号被称为具有全局作用域。默认情况下,符号存在于全局命名空间中,除非它在以关键字namespace 开头的块内定义,或者它是类的成员,或者是函数的局部变量:
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.
};
还请注意,name 可以被命名空间、函数或类定义的内部范围隐藏。因此名称空间N 内的名称a 将名称a 隐藏在全局名称空间中。同理,函数和类中的名称隐藏了全局命名空间中的名称。如果遇到这种情况,可以使用::a 来引用全局命名空间中定义的名称:
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
}
}
【讨论】:
a 在函数范围内声明,则不能从函数外部访问它。
“作用域”是比“命名空间”更笼统的术语。每个命名空间、类和代码块都定义了一个作用域,其中声明的名称可以在其中使用;命名空间是在类和函数之外声明的名称的容器。
“全局作用域”和“全局命名空间”可以或多或少互换使用;在命名空间中声明的名称的范围涵盖整个命名空间。如果您专门指的是命名空间,请使用“命名空间”,如果您指的是名称在其中的可见性,请使用“范围”。
【讨论】:
例如,当您声明一个全局变量int i 时,我们会说i is in the global namespace 和has the global namespace scope。就是这样。
摘自 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).
【讨论】:
Scope 表示对象的生命周期,您可以拥有一个只要程序执行就存在的全局变量,或者您可以拥有一个具有块范围的变量,只要该代码块执行就存在。考虑这个例子:
#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;
}
执行该语句时将打印local a is: 200,这显然是意料之中的,因为我们在main 中重新定义了a,它留在了它的封闭块的范围内。我们还打印了全局 ::a,它再次打印了预期值 100,因为我们已经请求了全局命名空间 ::。
命名空间语义大多是合乎逻辑的,它是一种将符号彼此隔离的方式,希望避免名称冲突,它不会影响对象的生命周期。
另一方面,作用域表示对象的生命周期,全局 a 在本地 a 之前出现,因为它的构造比 main 执行要早得多。但是,作用域也强制符号上的命名空间,但与namespace 所做的方式不同。有不同类型的作用域,global、class、function、block、file 等...
令人困惑的部分是作用域有时被重载以表示特定符号的可见性,这是从 C 中借用的东西,其中不存在命名空间的概念,而作用域被用来表示寿命和可见性。但是在 C++ 中,规则发生了一些变化,但术语 scope 仍然以相同的方式使用,因为这两种语言有很多共同的概念。
【讨论】:
@Dmitriy Ryajov
这个话题有点老了,但我想就此提供帮助。我认为你不应该让事情变得比实际情况更复杂。标识符的Scope 是计算机程序的一部分,其中标识符(指程序中某个实体的名称)可用于查找所引用的实体。因此,术语范围仅适用于标识符,我们不应将其与对象的生命周期混为一谈。它们有些联系,但不应混淆。对象的生命周期由我们为该对象分配内存的位置表示。因此,例如,如果在堆栈上分配了内存,它将在函数完成后立即释放。所以这取决于我们存储对象的位置,这表示它的生命周期。范围仅说:“这是一个对象的名称,我们可以在此之前为该对象使用此名称”。所以,正如我所说,术语 scope 仅用于对象的标识符,而生命周期是其他东西,由我们存储对象的位置表示。
另外,我想说一下与此密切相关的linkage。这有时也可能令人困惑。假设我们在translation unit 中有一些标识符,它们引用了一些对象。 other 翻译单元中的相同标识符是否将引用相同的实体由链接表示。因此,例如,如果一个标识符有一个外部链接,我们可以通过使用关键字extern 声明它来引用该标识符所引用的实体,但来自其他翻译单元。现在,假设我们不想在其他翻译单元中使用该实体。然后,实体将exist 直到程序完成,但是当我们不声明它时,我们将无法引用它。另请注意,现在我混合了链接和生命周期这两个术语。但这是因为只有global 实体具有外部链接。函数内部的标识符不能被程序的其他部分引用。
结论:始终尽量保持简单。我很惊讶不同的人如何以不同的方式谈论这些术语。单独编译的整个过程是混乱的,因为有多个术语几乎具有相同的含义,可能每个人都会卡在这一点上。
【讨论】: