【问题标题】:Global scope vs global namespace全局范围与全局命名空间
【发布时间】:2024-01-24 12:24:01
【问题描述】:

我看到了这两个短语的用法:全局作用域和全局命名空间。它们有什么区别?

【问题讨论】:

    标签: c++ namespaces scope terminology


    【解决方案1】:

    在 C++ 中,每个名称都有其不存在的范围。范围可以通过多种方式定义:它可以通过 namespacefunctionsclasses{ }.

    所以一个命名空间,无论是全局的还是其他的,都定义了一个范围。全局命名空间是指使用::,在这个命名空间中定义的符号被称为具有全局作用域。默认情况下,符号存在于全局命名空间中,除非它在以关键字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
        }
    }
    

    【讨论】:

    • 如果我们想在函数void f(){}中访问'a'(定义在命名空间N中)应该怎么做
    • @rimiro: 如果a 在函数范围内声明,则不能从函数外部访问它。
    • 所以命名空间是一种范围?
    • @JozefMikušinec.. 是的.. :-)
    【解决方案2】:

    “作用域”是比“命名空间”更笼统的术语。每个命名空间、类和代码块都定义了一个作用域,其中声明的名称可以在其中使用;命名空间是在类和函数之外声明的名称的容器。

    “全局作用域”和“全局命名空间”可以或多或少互换使用;在命名空间中声明的名称的范围涵盖整个命名空间。如果您专门指的是命名空间,请使用“命名空间”,如果您指的是名称在其中的可见性,请使用“范围”。

    【讨论】:

      【解决方案3】:

      例如,当您声明一个全局变量int i 时,我们会说i is in the global namespacehas 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).
      

      【讨论】:

        【解决方案4】:

        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 所做的方式不同。有不同类型的作用域,globalclassfunctionblockfile 等...

        令人困惑的部分是作用域有时被重载以表示特定符号的可见性,这是​​从 C 中借用的东西,其中不存在命名空间的概念,而作用域被用来表示寿命和可见性。但是在 C++ 中,规则发生了一些变化,但术语 scope 仍然以相同的方式使用,因为这两种语言有很多共同的概念。

        【讨论】:

        • 很好,您强调了范围是对象生命周期所固有的想法。
        【解决方案5】:

        @Dmitriy Ryajov

        这个话题有点老了,但我想就此提供帮助。我认为你不应该让事情变得比实际情况更复杂。标识符的Scope 是计算机程序的一部分,其中标识符(指程序中某个实体的名称)可用于查找所引用的实体。因此,术语范围仅适用于标识符,我们不应将其与对象的生命周期混为一谈。它们有些联系,但不应混淆。对象的生命周期由我们为该对象分配内存的位置表示。因此,例如,如果在堆栈上分配了内存,它将在函数完成后立即释放。所以这取决于我们存储对象的位置,这表示它的生命周期。范围仅说:“这是一个对象的名称,我们可以在此之前为该对象使用此名称”。所以,正如我所说,术语 scope 仅用于对象的标识符,而生命周期是其他东西,由我们存储对象的位置表示。

        另外,我想说一下与此密切相关的linkage。这有时也可能令人困惑。假设我们在translation unit 中有一些标识符,它们引用了一些对象。 other 翻译单元中的相同标识符是否将引用相同的实体由链接表示。因此,例如,如果一个标识符有一个外部链接,我们可以通过使用关键字extern 声明它来引用该标识符所引用的实体,但来自其他翻译单元。现在,假设我们不想在其他翻译单元中使用该实体。然后,实体将exist 直到程序完成,但是当我们不声明它时,我们将无法引用它。另请注意,现在我混合了链接和生命周期这两个术语。但这是因为只有global 实体具有外部链接。函数内部的标识符不能被程序的其他部分引用。

        结论:始终尽量保持简单。我很惊讶不同的人如何以不同的方式谈论这些术语。单独编译的整个过程是混乱的,因为有多个术语几乎具有相同的含义,可能每个人都会卡在这一点上。

        【讨论】: