使用未命名的命名空间代替静态全局变量

特别声明:
本文翻译自 Modern C++ Programming Cookbook 一书中的“Learning Modern Core Language Features”章节中的“Using unnamed namespaces instead of static globals”小节。
翻译此文纯粹作为学习之用,版权归原作者及出版社所有。

当你的程序连接时,程序越大,越有可能在本地文件中遇到名字冲突。在源文件中声明的函数或变量被转化成本地的编译单元,可能与另一个编译单元中声明的相同函数或变量产生名字冲突。这是因为所有没有声明为 static 的符号具有外部链接性,所以它们的名字在整个程序中必须是唯一的。针对该问题,典型的 C 解决方法是将这些符号声明为 static,将它们的链接性由外部改为内部,使它们只在一个编译单元中。在本节中,我们将看看针对该问题 C++ 的解决方法。

Getting ready

在本节中,我们将讨论一些概念,如全局函数,静态函数和变量,命名空间和编译单元。除这些之外,你还需要理解内部和外部链接的不同;这是本节的关键。

How to do it…

当你遇到这样的情况,需要将全局符号声明成 static 以避免链接问题时,优先使用未命名命名空间:

  1. 在你的源文件中声明一个没有名字的命名空间。
  2. 将全局函数或变量的定义放在未命名命名空间中,无须将它们声明为 static。

下面的例子显示了 2 个在不同编译单元的被称为 print()的 2 个函数;每一个都定义在一个未命名命名空间中:

 1// file1.cpp
2namespace
3{
4void print(std::string message)
5
{
6    std::cout << "[file1] " << message << std::endl;
7}
8}  
9
10void file1_run()
11
{
12    print("run");
13}
14
15// file2.cpp
16namespace
17{
18void print(std::string message)
19
{
20    std::cout << "[file2] " << message << std::endl;
21}
22
23
24void file2_run()
25
{
26    print("run");
27}

How it works…

当在一个编译单元中声明一个函数时,它具有外部链接性。这就是说两个不同编译单元中的两个具有相同名字的函数将生成一个链接错误,因为不能存在具有相同名字的两个符号。这个问题在 C 中已经解决,在 C++ 中也解决了部分,就是将函数或变量声明成 static 从而将它的链接性从外部改为内部。在这种情况下,它的名字不再导出到外部的编译单元中,从而避免了这个链接问题。

C++ 中合适的解决方法是使用未命名命名空间。当你定义了像之前上面显示的命名空间时,编译器将其转换为如下形式:

 1// file1.cpp
2namespace _unique_name_ { }
3using namespace _unique_name_;
4namespace _unique_name_
5{
6    void print(std::string message)
7    
{
8        std::cout << "[file1] " << message << std::endl;
9    }
10}  
11
12void file1_run()
13
{
14    print("run");
15}

第一,声明了一个具有唯一名字的命名空间()。此时该命名空间是空的,这一行的目的是创建该命名空间。
第二,using 指示符将 unique_name 命名空间中的所有东西引入到当前命名空间。
第三,该命名空间使用编译器生成的名字,定义与原来的代码一样(当它没有名字时)。

通过在未命名的命名空间中定义编译单元中的 print() 函数,它们只有本地可见性,然而它们的外部链接性不再产生链接错误,应为它们现在有了外部唯一的名字。

在调用模板的可能更加晦涩的情况下,未命名命名空间也能正常工作。模板参数的名字不能有内部链接性,因此不能使用 static 变量。换句话说,在未命名命名空间中的符号具有外部链接性并且可以用作模板参数。这个问题显示在下面的例子中,声明的 t1 产生一个编译错误,因为非类型参数表达式具有内部链接性。然而,t2 是正确的,因为 Size2 具有外部链接性:

 1template <int const& Size>
2class test {};
3
4static int Size1 = 10;
5
6namespace
7{
8    int Size2 = 10;
9}
10
11test<Size1> t1; // error
12test<Size2> t2; // ok

- - - End - - -


欢迎扫码订阅我的微信公众号,阅读其它相关文章。
欢迎扫码订阅我的微信公众号,阅读其它相关文章。

本文作者: Lzl678
本文链接: https://www.cnblogs.com/Lzl678/p/10902685.html
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

posted @ 2019-05-21 22:22  Lzl678  阅读(1142)  评论(0编辑  收藏  举报