在阅读 Bruce Eckel 关于命名空间的“Thinking in C++”时,我遇到了以下语句:
但是,您几乎永远不会在头文件中看到 using 指令(至少不在范围之外)。原因是 using 指令消除了对该特定命名空间的保护,并且效果一直持续到当前编译单元结束。如果您将 using 指令(范围之外)放在头文件中,则意味着这种“命名空间保护”的丢失将发生在包含此头文件的任何文件中,这通常意味着其他头文件。
您愿意通过一些简单的例子帮助我理解上述陈述吗?
在阅读 Bruce Eckel 关于命名空间的“Thinking in C++”时,我遇到了以下语句:
但是,您几乎永远不会在头文件中看到 using 指令(至少不在范围之外)。原因是 using 指令消除了对该特定命名空间的保护,并且效果一直持续到当前编译单元结束。如果您将 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
... 并将其放入您#include
之前的头文件中main()
。没有任何变化:您仍然有错误。因此,就像对于独立程序一样,带有using
语句的头文件可能会给包含它们的其他代码带来麻烦,从而使它们的某些语句模棱两可。
不过,这可能会带来更大的痛苦,因为可以通过任意大量的依赖代码直接或间接地包含标头,并且...
using
从标题中删除语句,或<string>
或任何其他影响的标题的更改std::
...可能会破坏代码,包括有问题的标头。任何一个问题都可能导致相关代码无法编译,并且在尝试进行另一次编译之前甚至可能不会注意到问题。此外,由于该using
语句而遭受痛苦的人可能没有文件系统/代码存储库权限、公司权限等来using
从标头中删除该语句,也没有修复其他受影响的客户端代码。
也就是说,如果标头仅在类或函数内部具有“使用”功能,则对超出该范围的代码没有影响,因此对 std:: 更改的潜在影响会大大降低。
如果标头包含using namespace std
,则该名称空间中的所有内容都将添加到包含标头的每个模块中的全局名称空间。
std
这意味着您永远不能在任何这些模块的全局命名空间中声明一个函数或定义一个与函数/类具有相同名称(以及函数的兼容参数)的类。
从“C++ Primer,第五版”复制以下段落:
标头内的代码通常不应使用
using
声明。原因是标题的内容被复制到包含程序的文本中。如果头文件有 using 声明,则包含该头文件的每个程序都会获得相同的 using 声明。因此,不打算使用指定库名称的程序可能会遇到意外的名称冲突。
那么,使用命名空间有什么意义。这是为了避免名称冲突的风险。
假设您有一些非常常见的类名,例如 FooBar。如果您使用多个库,则库 A 中的 FooBar 与库 B 中的 FooBar 存在冲突的风险。为此,我们使用两个不同的命名空间 A 和 B,将 FooBars 从全局命名空间移动到 A::FooBar 和 B::FooBar (因此它们彼此分开)。
如果您然后将using A;
andusing B;
放入标题中,这会将 A::FooBar 和 B::FooBar 移动到 FooBar 中,从而恢复冲突,首先消除使用命名空间的好处。