跳转到内容

C++ 编程

来自 Wikibooks,开放世界中的开放书籍

命名空间关键字允许您创建一个新的作用域。名称是可选的,可以省略以创建未命名命名空间。创建命名空间后,您必须显式引用它或使用using关键字。命名空间由namespace块定义。

语法
    namespace name {
    declaration-list;
    }

在许多编程语言中,命名空间标识符的上下文。C++ 可以处理语言中的多个命名空间。通过使用namespace(或using namespace关键字),可以提供一种干净的方式将代码聚合到一个共享的标签下,以防止命名冲突或仅仅为了方便回忆和使用非常特定的作用域。除了“命名空间”之外,还有其他“名称空间”;这可能会令人困惑。

名称空间(注意那里的空格),正如我们将看到的,通过提供一种简单的方法来区分正在调用/使用什么,超越了作用域的概念。正如我们将看到的,类也是名称空间,但它们不是命名空间。

注意
仅在方便或真正需要时(例如,相关代码的聚合)使用namespace,不要以使代码对您和其他人过于复杂的方式使用它

示例
namespace foo {
  int bar;
}

在此块内,标识符可以按其声明的方式使用。在此块之外,必须添加namespace说明符(即,它必须是限定的)。例如,在namespace foo之外,bar必须写成foo::bar

C++ 包含另一个构造,它使这种冗长性变得不必要。通过将行using namespace foo;添加到代码段中,不再需要前缀foo::

未命名命名空间
[编辑 | 编辑源代码]

没有名称的namespace称为未命名命名空间。对于这样的namespace,将为每个翻译单元生成一个唯一的名称。无法将using关键字应用于未命名命名空间,因此未命名命名空间的工作方式就好像已将using关键字应用于它一样。

语法
    namespace {
    declaration-list;
    }
命名空间别名
[编辑 | 编辑源代码]

您可以为命名空间创建新名称(别名),包括嵌套命名空间

语法
   namespace identifier = namespace-specifier;


使用命名空间
[编辑 | 编辑源代码]
使用
using namespace std;

使用-指令指示程序中使用的但未声明的任何名称都应在“标准(std)”命名空间中查找。

注意
在头文件中使用使用指令总是一个坏主意,因为它会影响对该头文件的每次使用,并且会使其在其他派生项目中的使用变得困难;没有办法“撤消”或限制该指令的使用。另外,不要在#include指令之前使用它。

要使来自命名空间的单个名称可用,以下使用-声明存在

using foo::bar;

在此声明之后,名称bar可以在当前命名空间内使用,而不是更冗长的版本foo::bar。请注意,程序员经常互换使用术语“声明”和“指令”,尽管它们在技术上的含义不同。

最佳实践是使用狭义的第二种形式(使用声明),因为广义的第一种形式(使用指令)可能会使比所需更多的名称可用。示例

namespace foo {
  int bar;
  double pi;
}
 
using namespace foo;
 
int* pi;
pi = &bar;  // ambiguity: pi or foo::pi?

在这种情况下,声明using foo::bar;只会使foo::bar可用,避免了pifoo::pi的冲突。此问题(相同名称的变量或函数的冲突)称为“命名空间污染”,原则上应尽可能避免。

使用-声明可以出现在许多不同的位置。其中包括

  • 命名空间(包括默认命名空间)
  • 函数

一个使用-声明使名称(或命名空间)在声明的作用域内可用。示例

namespace foo {
  namespace bar {
   double pi;
  }

  using bar::pi;
  // bar::pi can be abbreviated as pi
}
 
// here, pi is no longer an abbreviation. Instead, foo::bar::pi must be used.

命名空间是分层的。在假设的命名空间food::fruit中,标识符orange引用food::fruit::orange(如果存在),否则引用food::orange(如果存在)。如果两者都不存在,orange则引用默认命名空间中的标识符。

未在命名空间中显式声明的代码被认为在默认命名空间中。

命名空间的另一个属性是它们是开放的。一旦声明了命名空间,就可以重新声明(重新打开)它,并且可以添加命名空间成员。示例

namespace foo {
  int bar;
}
 
// ...
 
namespace foo {
  double pi;
}

命名空间最常用于避免命名冲突。尽管在最近的 C++ 代码中广泛使用了命名空间,但大多数旧代码未使用此功能。例如,整个标准库是在命名空间std中定义的,在该语言的早期标准中,在默认命名空间中。

对于较长的命名空间名称,可以定义较短的别名(命名空间别名声明)。示例

namespace ultra_cool_library_for_image_processing_version_1_0 {
  int foo;
}
 
namespace improc1 = ultra_cool_library_for_image_processing_version_1_0;
// from here, the above foo can be accessed as improc1::foo

存在一个特殊的命名空间未命名命名空间。此命名空间用于特定源文件或其他命名空间私有的名称

namespace {
  int some_private_variable;
}
// can use some_private_variable here

在周围的作用域中,可以访问未命名命名空间的成员,而无需限定,即无需使用命名空间名称和::(因为命名空间没有名称)。如果周围的作用域是命名空间,则可以将成员视为并访问为其成员。但是,如果周围的作用域是文件,则无法从任何其他源文件访问成员,因为无法将文件命名为作用域。未命名命名空间声明在语义上等效于以下构造

namespace $$$ {
  // ...
}
using namespace $$$;

其中$$$是由编译器生成的唯一标识符。

您可以将未命名命名空间嵌套在普通命名空间中,反之亦然,您也可以嵌套两个未命名的命名空间。

namespace {

  namespace {
    // ok
  }

}

注意
如果启用了代码中命名空间的使用,则所有代码都将使用它(您无法定义将使用和排除其他部分的部分),但是,您可以使用嵌套命名空间声明来限制其作用域。

由于空间限制,我们实际上无法显示命名空间命令的正确使用方法:它需要一个非常大的程序才能显示其有效工作。但是,我们可以轻松地说明其概念本身。

// Namespaces Program, an example to illustrate the use of namespaces
#include <iostream>

namespace first {
  int first1;
  int x;
}

namespace second {
  int second1;
  int x;
}

namespace first {
  int first2;
}

int main(){
  //first1 = 1;
  first::first1 = 1;
  using namespace first;
  first1 = 1;
  x = 1;
  second::x = 1;
  using namespace second;

  //x = 1;
  first::x = 1;
  second::x = 1;
  first2 = 1;

  //cout << 'X';
  std::cout << 'X';
  using namespace std;
  cout << 'X';
  return 0;
}

我们将从程序的开头向下移动到程序的末尾检查代码,依次检查代码片段。

#include <iostream>

这仅包含iostream库,以便我们可以使用std::cout将内容打印到屏幕上。

namespace first {
  int first1;
  int x;
}

namespace second {
  int second1;
  int x;
}

namespace first {
  int first2;
}

我们创建一个名为first的命名空间,并在其中添加两个变量first1x。然后我们关闭它。然后我们创建一个名为second的新命名空间,并在其中放入两个变量:second1x。然后我们重新打开命名空间 first,并在其中添加另一个名为first2的变量。可以像这样多次重新打开命名空间,以添加额外的名称。

  main(){
1  //first1 = 1;
2  first::first1 = 1;

主程序的第一行被注释掉了,因为它会导致错误。为了访问第一个命名空间中的名称,我们必须在变量名称前加上其命名空间的名称和两个冒号;因此,主程序的第二行不是语法错误。变量的名称在作用域内:它只需要以这种特殊的方式被引用,然后才能在此处使用。因此,这将全局名称列表分割成组,每组都有自己的前缀名称。

3  using namespace first;
4  first1 = 1;
5  x = 1;
6  second::x = 1;

主程序的第三行引入了using namespace命令。此命令将第一个命名空间中的所有名称拉入作用域。然后可以从那里以通常的方式使用它们。因此,程序的第四行和第五行可以编译而不会出错。特别是,变量x现在可用:为了访问第二个命名空间中的另一个变量x,我们将它称为second::x,如第六行所示。因此,这两个名为x的变量可以分别引用,就像它们在第五行和第六行一样。

7  using namespace second;
8  //x = 1;
9  first::x = 1;
10 second::x = 1;

然后我们再次使用using namespace命令将命名空间名为second中的声明拉入。下一行被注释掉了,因为它现在是一个错误(而之前是正确的)。由于这两个命名空间都已被引入全局名称列表,因此变量x现在变得不明确,并且需要仅以第九行和第十行所示的限定方式进行讨论。

11 first2 = 1;

主程序的第十一行表明,即使first2是在命名空间名为first的单独部分中声明的,它与命名空间 first中的其他变量具有相同的状态。可以根据需要多次重新打开命名空间。当然,作用域的通常规则适用:在同一个命名空间中两次声明相同的名称是非法的。

12 //cout << 'X';
13 std::cout << 'X';
14 using namespace std;
15 cout << 'X';
}

计算机中有一组特殊的文件中定义了一个命名空间。它的名称是std,所有系统提供的名称,例如cout,都在该命名空间中的一系列不同文件中声明:这是一个非常大的命名空间。请注意,程序最顶部的#include语句并没有完全引入命名空间:名称在那里,但仍然必须以限定形式引用。第十二行必须被注释掉,因为目前系统提供的名称(如cout)不可用,除非以限定形式std::cout,如第十三行所示。因此,我们需要像第十四行这样的行:写入该行后,所有系统提供的名称都可用,如程序的最后一行所示。此时,我们已将三个命名空间的名称合并到程序中。

如示例程序所示,根据需要引入所需的声明,并排除不需要的声明,并且可以使用带双冒号的限定形式以受控的方式引入它们。这为大型程序提供了所需的更好的名称控制。在上面的示例中,我们只使用了变量的名称。但是,命名空间也同样根据需要控制过程和类的名称。

华夏公益教科书