# 二、名称空间

## 全局命名空间

考虑以下代码:

示例:全局名称空间示例\全局名称空间示例. cpp

```cpp
    #include <iostream>
    #include <ostream>
    #include "../pchar.h"

    int g_x = 10;

    int AddTwoNumbers(int a, int b)
    {
          return a + b;
    }

    int _pmain(int /*argc*/, _pchar* /*argv*/[])
    {
          int y = 20;
          std::wcout << L"The result of AddTwoNumbers(g_x, y) where g_x = " <<
                g_x << L" and y = " << y << L" is " << AddTwoNumbers(g_x, y) <<
                L"." << std::endl;

          if (true == 1)
          {
                std::wcout << L"true == 1!" << std::endl;
          }
          return 0;
    }

```

* * *

C++ 允许您拥有不属于类的方法。这些叫做函数。更具体地说，它们通常被称为独立函数，因为类方法的 C++ 术语是类成员函数。我们将在后面的章节中更详细地讨论函数。

* * *

在前面的示例中，我们定义了两个函数，`AddTwoNumbers`和`wmain`。这两个函数都在全局命名空间中。全局命名空间是基础级别，程序中的所有其他东西都存在于其中。C++，由于其 C 的传统，允许您定义全局命名空间中的任何东西(因此您可以定义命名空间、类、结构、变量、函数、枚举和模板)。

C# 也有全局命名空间的概念，但是除了命名空间和类型之外，它不允许您在其中定义任何东西。在前面的例子中，我们有语句`int g_x = 10;`，它在全局命名空间中定义了一个名为 g_x 的整数，并为其赋值 10。这就是通常所说的全局变量。在 C# 中，全局变量是非法的。

简而言之，我使用过的每一种编程语言都有它不好的地方——语言允许的东西，但通常会导致问题。这些问题包括难以调试的问题、长期未被注意的细微错误、可维护性问题、可读性问题，以及所有其他令人沮丧的事情，这些事情增加了许多开发时间，却没有任何好处。C++ 也不例外。当我们遇到符合这个描述的东西时，我会尽我所能把它叫出来。这是其中之一。

全局变量不好。尽可能避开他们。在 C++ 中使用它们有一个常见的约定，就是在变量名前加`g_`前缀，就像前面的例子一样。虽然这有助于提醒您和其他程序员这是一个全局变量，但这并不能改变它是一个全局变量的事实，它有我描述的所有问题。这不是一本关于糟糕编程实践的书，所以我不会花时间解释[为什么全局变量是糟糕的](http://stackoverflow.com/questions/484635/are-global-variables-bad)。你只需要知道这个特性存在于 C++ 中，但是你应该尽可能避免使用它。

## 作用域解析运算符:“”

在 C++ 中，`::`是作用域解析运算符。它用于分隔嵌套的命名空间，用于分隔类型及其命名空间，用于分隔成员函数和变量及其类型。

请注意，它仅在执行以下操作时的最后一种情况下使用:

*   定义成员函数。
*   在成员函数定义中访问基类的成员。
*   访问静态成员函数或变量。
*   为静态成员变量赋值。
*   获取成员函数的地址。

在其他情况下，您可以使用`.`运算符或`->`运算符，具体取决于您是直接访问成员还是通过指针访问成员。

这看起来很复杂，因为 C# 只是使用`.`运算符来实现所有在 C++ 中使用`::`、`.`和`->`的目的。

| ![](img/image001.png) | 注意:我们将讨论。和->运算符。现在，您只需要知道它们用于访问实例成员变量和非静态成员函数(根据是否使用指针来使用)。 |

在大多数情况下，你会没事的。唯一可能出错的地方是，如果您试图通过使用`.`运算符而不是`::`运算符来访问基类成员，或者如果您试图使用除了`::`之外的其他东西来指定枚举成员。如果你曾经编译你的程序并收到一个语法错误，抱怨“缺少”在“.”之前“，这是一个很好的赌注你用了一个`.`而你应该用一个`::`来代替。

## 定义名称空间

命名空间的定义方式与 C# 非常相似。这里有一个例子:

样例:namespacessample \ namespacessample . CPP

```cpp
    #include <iostream>
    #include <ostream>
    #include <string>
    #include "../pchar.h"

    using namespace std;

    namespace Places
    {
          namespace Cities
          {
                struct City
                {
                      City(const wchar_t* name)
                      {
                            Name = wstring(name);
                      }

                      wstring Name;
                };
          }
    }

    int _pmain(int /*argc*/, _pchar* /*argv*/[])
    {
          auto nyc = Places::Cities::City(L"New York City");

          wcout << L"City name is " << nyc.Name.c_str() << L"." << endl;

          return 0;
    }

```

正如您所看到的，我们可以嵌套名称空间，然后使用范围解析操作符访问这些名称空间和其中包含的类型。您还会注意到我们使用了`auto`关键字。这是 C# 的`var`关键字的 C++ 等价物。

## 使用命名空间指令

查看**命名空间示例**，注意我们已经包含了行`using namespace std;`。这一行看起来很像 C# 中的`using`指令，而且是；然而，在 C# 中,`using`指令会自动将该命名空间中任何被引用程序集中的任何类型纳入范围。相比之下，在 C++ 中,`using`指令只将包含的头文件中的类型纳入范围，而不是将链接到您的程序或库的库中的所有类型。你问的头文件是什么？这个问题为下一章提供了很好的线索。

| ![](img/image001.png) | 注意:不要在头文件中包含 using 指令。如果您这样做了，您不仅可以将该命名空间中的类型和命名空间导入到头文件中，还可以将它们导入到包含头文件的任何源文件或头文件中。这导致了非常严重的名称空间污染问题。接下来我们将讨论头文件，所以任何不清楚的都应该有意义。只要记住在头文件中有一个`using namespace`指令是一个坏主意；只在源代码文件中使用它们。 |