A.4 — C++ FAQ
Key Takeaway
有一些问题往往被问了一遍又一遍。本FAQ将尝试回答最常见的问题。
问: 为什么不应该使用 “using namespace std
”?
语句 using namespace std;
使用了using 指令——它将某个命名空间中的所有标识符都导入到当前命名空间。
你可能看过这样的代码:
1 2 3 4 5 6 7 8 9 10 |
|
这么做之后,我们就可以不必显式指明std
的命名空间,再也不用写 std::
了。在上面的程序中,只需要使用 cout
而不需要使用 std::cout
。听起来很不错对吧?
然而,当编译器遇到 using namespace std
时,它会将在 namespace std
中找到的每一个标识符都导入全局作用域(因为using指令就放在全局作用域中)。这带来了3个主要挑战:
- 你所选择的标识符与
std
名称空间中已经存在的标识符之间发生命名冲突的几率大大增加; - 标准库的新版本可能会破坏你当前可以工作的程序。这些未来的版本可能会引入导致新的命名冲突的名称,或者在最坏的情况下,程序的行为可能会悄无声息地出乎意料地改变!
- 缺少
std::
前缀会使读者难以区分哪些名字属于 std 库哪些是用户定义的。
因此,我们推荐避免使用 using namespace std
(以及其他 using 语句)。通过它节省的打字时间和可能带来的风险相比是不划算的。
相关内容
参见 6.12 - using 声明和 using 指令 获取更多信息。
问:为什么使用某些功能时不需要包含头文件?
头文件可以“#include
”其他头文件。因此,当我们包含一个头文件时,同时就可以获得它包含的所有附加头文件(以及那些头文件包含的所有头文件)。所有没有显式包含的附加头文件称为“传递包含”。
比方说,main.cpp
中 #included <iostream>
,同时你的编译器在 <iostream>
中 #included <XXX>
(或者其他头文件 #included <XXX>
)。
即使它可以在编译器上编译,也不应该依赖它。因为你能够编译的东西可能不能在其他编译器上编译,甚至不能在你的编译器的未来版本上编译。
没有办法在这种情况发生时发出警告,或防止它发生。我们能做的就是为所使用的所有东西显式地包括适当的头文件。在几个不同的编译器上编译你的程序可能有助于识别被其他编译器传递包含的头文件。
相关内容
参见 2.11 - 头文件
问:为什么(产生未定义行为的代码)有这样的结果?
当程序执行非C++标准定义的操作时,会发生未定义行为。实现未定义行为的代码可能会出现以下任何症状:
- 程序每次运行都会产生不同的结果;
- 程序始终产生相同的错误结果;
- 程序行为不一致(有时产生正确的结果,有时不);
- 程序似乎在工作,但在程序后面产生了不正确的结果;
- 程序立即崩溃或一段时间后崩溃;
- 程序可以在一些编译器上工作,但不能在其他编译器上工作;
- 程序可以工作,但是在改变一些其他看起来不相关的代码后就不能工作了
当然,你的代码也可能会产生正确的结果。
读者经常会在提问中问到为什么在他们的系统上会产生特定的现象,是什么原因导致的。在大多数情况下,这很难说,因为生成的结果可能取决于当前程序状态、编译器设置、编译器如何实现某个特性、计算机的架构和/或操作系统。例如,如果打印一个未初始化的变量的值,可能会得到垃圾,或者总是得到一个特定的值。这取决于它是什么类型的变量,编译器如何在内存中布局变量,以及在此之前内存中有什么(可能受到操作系统或在此点之前的程序状态的影响)。
虽然回答上述问题可能很有趣,但总体上这样的答案并没有什么用用(而且可能会在其他东西发生变化时发生变化)。这就像在问:“当我把安全带穿过方向盘并连接到油门上时,为什么我在雨天转头时,汽车会向转?”最好的答案不是对正在发生的事情做出解释,而是“不要那样做”。
相关内容
问:我尝试编译一个例子,它本应该工作却编译报错了,为什么?
最常见的原因是,你的项目正在使用语言标准是不对的。
C++在每个新的语言标准中都引入了许多新特性。如果我们的一个示例使用C++ 17中引入的特性,但你的程序正在使用C++ 14语言标准编译,那么它将无法编译,因为我们正在使用的特性不存在。尝试标准设置为编译器支持的最新版本,看看这是否解决了问题。
也有可能你的编译器还不支持特定的特性,或者在某些情况下有一个bug导致其无法使用。在这种情况下,尝试将编译器更新到可用的最新版本。