为什么在C ++中包含“使用名称空间”到头文件是一个坏主意?

在阅读Bruce Eckel关于命名空间的“Thinking in C ++”时,我遇到了以下的说法:

然而,你几乎从不会在头文件中看到一个using指令(至less不在范围之外)。 原因是使用指令消除了对那个特定名字空间的保护,并且效果持续到当前编译单元结束。 如果你在一个头文件中放置一个using指令(在一个范围之外),这意味着这个“名字空间保护”的丢失将出现在任何包含这个头文件的文件中,这通常意味着其他的头文件。

请您帮助我用一些简单的例子来理解上述说法吗?

考虑这个程序:

line# 1 #include <string> 2 3 using namespace std; 4 5 struct string { const char* p; }; // Beware: another string! 6 7 int main() 8 { 9 string x; // Error: ambiguous - which string is wanted? 10 } 

如果你尝试编译它,你会看到错误:

 g++ using.cc -o using using.cc: In function `int main()': using.cc:9: error: use of `string' is ambiguous using.cc:5: error: first declared as `struct string' here /usr/lib/gcc/i386-redhat-linux/3.4.6/../../../../include/c++/3.4.6/bits/stringfwd.h:60: error: also declared as `typedef struct std::basic_string<char, std::char_traits<char>, std::allocator<char> > std::string' here using.cc:9: error: `string' was not declared in this scope using.cc:9: error: expected `;' before "x" 

这里的问题是,当main()指定string x; ,编译器不确定是否需要用户定义的::string或包含的std::string

现在想象一下,将程序的最上面部分…第1到第5行 – 直到struct string包括struct string放到头文件中,然后在main()之前#include 。 没有改变:你仍然有一个错误。 所以,就像独立程序一样, using语句的头文件可能会给包含它们的其他代码带来麻烦,使得它们的一些语句不明确。

这可能是一个更大的痛苦,因为标题可以被直接或间接地包含在任意大量的依赖代码中,并且…

  • 从头部删除using语句,或
  • 更改<string>的内容,或者任何其他影响std::

…可能会破坏代码,包括有问题的标题。 任何一个问题都可能导致相关的代码不可编译,甚至直到另一个编译尝试才会被注意到。 此外,由于using声明而遭受的人可能没有文件系统/代码库权限,公司权限等从头中删除using语句,也不修复其他受影响的客户端代码。

也就是说,如果一个头只有在一个类或函数内部使用“”,那么对这个范围之外的代码没有任何影响,所以std ::变化的潜在影响大大减less了。

如果一个头包含using namespace std ,则来自该名称空间的所有内容都会在每个包含该头的模块中添加全局名称空间。

这意味着你永远不能声明一个函数,或者在任何这些模块的全局名字空间中定义一个具有相同名字的类(和一个函数的兼容参数)作为一个std函数/类。

那么,使用名称空间有什么意义呢? 这是为了避免名称冲突的风险。

假设你有一些非常普通的类名,例如FooBar。 如果使用多个库,则库A中的FooBar与库B中的FooBar相冲突。为此,我们使用两个不同的名称空间A和B将FooBars从全局名称空间移动到A :: FooBar和B :: FooBar (所以他们保持彼此分开)。

如果你再using A;using B; 在头文件中,这会将A :: FooBar和B :: FooBar移动到FooBar,从而将冲突恢复到原来的位置。

从“C ++入门,第五版”复制以下段落:

代码里面的标题通常不应该using声明。 原因是标题的内容被复制到包含程序的文本中。 如果一个头文件有一个using声明,那么包含这个头文件的每个程序都会使用声明获得相同的结果。 因此,不打算使用指定的库名称的程序可能会遇到意外的名称冲突。