购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.11 使用匿名命名空间来代替静态全局空间

程序越大,当程序链接到多个编译单元时,发生命名冲突的可能性也就越大。在源文件某编译单元中声明的函数或变量可能会与在另一个编译单元中声明的类似函数或变量发生冲突。

这是因为所有没有被声明为静态的符号都有外部链接,而且它们的名称在整个程序中必须是唯一的。针对此问题,典型的C解决方案是将这些符号声明为静态的,将它们的链接从外部更改为内部,从而使它们成为编译单元的本地符号;另一种方法是在名称前面加上它们所属的模块或库的名称。在本节中,我们将讨论该问题的C++解决方案。

1.11.1 准备工作

在本节中,我们将讨论全局函数和静态函数,以及变量、命名空间和编译单元等概念,我们希望读者对这些概念有一个基本的了解。除了这些,读者还要了解内部链接和外部链接之间的区别,这是本节的核心内容。

1.11.2 使用方式

当需要将全局符号声明为静态的以避免链接问题时,应该首选匿名命名空间:

❍ 在源文件中声明一个匿名命名空间。

❍ 将全局函数或变量的定义放在匿名命名空间中,但不要将它们设为static。

下面的例子显示在两个不同的编译单元中有两个名为print()的函数,它们都定义在匿名命名空间中:

1.11.3 工作原理

当函数在编译单元中被声明时,它具有外部链接属性。这意味着来自两个不同编译单元的两个具有相同名称的函数将产生链接错误,因为不可能有两个具有相同名称的符号。在C语言中,解决这个问题的方法是,将函数或变量声明为静态的,并将其链接从外部更改为内部。在这种情况下,它的名称不再被导出到编译单元之外,避免了链接问题。

在C++中,一种合适的解决方法是使用匿名命名空间。当像前面那样定义一个命名空间时,编译器会把它转换成如下形式:

首先,它声明了一个具有唯一名称的命名空间(名称是什么以及如何生成该名称是编译器实现的细节,我们无须关注),此时命名空间还是空的,这一行代码的目的是构建命名空间。其次,使用using关键字在当前命名空间引入_unique_name_命名空间中的所有内容。最后,名称由编译器生成的命名空间的定义与原始源代码中的一样(当时命名空间是匿名的)。

通过在匿名命名空间中定义编译单元局部print()函数,它们仅具有本地可见性,但它们的外部链接不再产生链接错误,因为它们现在具有外部唯一名称。

匿名命名空间还可以用于更复杂的情形,比如模板。一方面,在C++11之前,模板非类型实参不能是具有内部链接的名称,因此不可能使用静态变量。另外,匿名命名空间中的符号具有外部链接,可以用作模板实参。虽然C++11中取消了模板非类型实参的链接限制,但它仍然存在于最新版本的VC++编译器中。该问题如下所示:

在此代码片段中,t1变量的声明导致了一个编译错误,因为非类型实参表达式Size1有内部链接。t2变量的声明是正确的,因为Size2有外部链接(注意,用Clang和GCC编译此代码片段不会产生错误)。

1.11.4 延伸阅读

❍ 阅读1.12节,以了解如何使用内联命名空间和条件编译来管理源代码版本。 VW9pkU9htdqCKFwg42lVMc1mzsp38wuvNAvWlinr9Ol9w8uy7bnAEBK6loT/D2J0

点击中间区域
呼出菜单
上一章
目录
下一章
×

打开