在 c + + 头中“使用名称空间”

在我们所有的 c + + 课程中,所有的教师都把 using namespace std;放在 .h文件中 #include之后。这对我来说似乎是危险的,因为在另一个程序中包含这个头,我将得到导入到我的程序中的名称空间,可能没有意识到,打算或想要它(头包含可以是非常深的嵌套)。

所以我的问题是双重的: using namespace不应该在头文件中使用,我是对的吗? 或者有什么方法可以撤消它,比如:

//header.h
using namespace std {
.
.
.
}

同样还有一个问题: 一个头文件 #include是否应该包含它对应的 .cpp文件所需要的所有头文件,只包含头定义所需要的那些头文件,让 .cpp文件 #include占据剩余的头文件,还是不包含头文件,并将它所需要的所有东西都声明为 extern
这个问题背后的原因和上面一样: 我不希望在包含 .h文件时出现意外。

另外,如果我是对的,这是一个常见的错误吗?我的意思是在现实世界的编程和“真正的”项目中。

谢谢你。

104783 次浏览

你说得对。任何文件都应该只包含该文件所需的头。至于“在现实世界的项目中,做错事情是常见的吗?”哦,是的!

你是对的,using namespace在头部是危险的。 我不知道该怎么办。 很容易检测到它,但只要在头文件中搜索 using namespace。 由于最后一个原因,它在实际项目中并不常见。更有经验的同事会很快抱怨,如果有人做了这样的事情。

在实际的项目中,人们试图最小化包含的文件数量,因为包含的文件越少,编译的速度就越快。这样可以节省大家的时间。但是,如果头文件假定某些内容应该在它之前包含,那么它应该包含它本身。否则,它使头部不能自包含。

在标题中包含标题时需要小心。在大型项目中,它可以创建一个非常复杂的依赖链,触发比实际需要更大或更长的重新构建。查看 这篇文章它的后续行动,了解更多关于 C + + 项目中良好物理结构的重要性。

只有在绝对需要的时候(无论何时需要一个类的完整定义) ,你才应该在标题中包含标题,并且尽可能地使用前向声明(当需要的类是一个指针或引用时)。

至于名称空间,我倾向于在头文件中使用明确的名称空间范围,并且在 cpp 文件中只放置一个 using namespace

你绝对不应该在标题中使用 using namespace,因为它可以意外地改变包含该标题的任何其他文件中代码的含义。没有办法撤销 using namespace这也是它如此危险的另一个原因。我通常只是使用 grep或类似的方法来确保 using namespace不会在头中被调用,而不是尝试更复杂的方法。可能静态代码检查器也标记了这个。

标头应该只包含需要编译的标头。实施这一点的一个简单方法是始终将每个源文件自己的头放在任何其他头之前。如果头部不是自包含的,那么源文件将无法编译。在某些情况下,例如引用库中的实现细节类,您可以使用转发声明而不是 #include,因为您可以完全控制这种转发声明类的定义。

我不确定我会称之为常见,但它肯定会偶尔出现,通常是由新程序员编写的,他们没有意识到这样做的负面后果。通常情况下,只要对风险进行一点教育,就可以解决任何问题,因为解决起来相对简单。

就像编程中的所有事情一样,实用主义应该战胜教条主义。

只要您在整个项目范围内做出决策(“我们的项目广泛使用 STL,我们不希望必须使用 std: : .”来预先准备所有内容”)我不觉得有什么问题。毕竟,你所冒的唯一风险就是名称冲突,而且随着 STL 的普及,这不太可能成为一个问题。

另一方面,如果这是一个开发人员在单个(非私有)头文件中做出的决定,我可以看到它将如何在团队中产生混淆,应该避免。

萨特和亚历山德里斯库的 C + + 编码标准: 101条规则、指南和最佳实践里第59条:

59. 不要在头文件中或在 # include 之前使用名称空间。

命名空间 using是为了方便您使用,而不是为了让您将其强加给其他人: 绝不要在 #include指令之前编写 using声明或 using指令。

推论: 在头文件中,不要编写名称空间级别的 using指令或 using声明; 而是显式地对所有名称进行名称空间限定。

头文件是一个或多个源文件中的来宾文件。包含 using指令和声明的头文件也带来了吵闹的伙伴。

一个 using 声明带来一个伙伴。using 指令引入了名称空间中的所有伙伴。您的教师使用 using namespace std;是一个使用指令。

更严重的是,我们使用名称空间来避免名称冲突。头文件旨在提供一个接口。大多数头文件都不知道现在或将来可能包含哪些代码。为了内部方便,在头中添加 using语句将这些方便的名称强加到该头的所有潜在客户机上。这会导致名字冲突。这太没礼貌了。

你可在此查阅戈达德太空飞行中心编码标准(适用于 C 及 C + +)。事实证明,这比过去要困难一些——看看 SO 问题的最新答案:

GSFC C + + 编码标准表示:

3.3.7每个头文件应该 #include它需要编译的文件,而不是强迫用户 #include所需要的文件。#includes应该限制在头需要的范围内; 其他的 #includes应该放在源文件中。

第一个相互参照的问题现在包括从 GSFC C 编码标准的引用,和基本原理,但实质最终是相同的。

我相信,如果你在嵌套名称空间中编写声明,你可以安全地使用 C + + 标题中的“ using”,如下所示:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
/*using statements*/


namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
{
/*declarations*/
}
}


using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

这应该只包括在“ DECLARATION _ WITHNO _ NAMESPACES _ USED _ INCLUDED”中声明的内容,而不包括所使用的名称空间。

关于“是否有某种方法可以撤销[ using声明] ?”

我认为有必要指出,using声明受范围的影响。

#include <vector>


{   // begin a new scope with {
using namespace std;
vector myVector;  // std::vector is used
}   // end the scope with }


vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

所以实际上是的。通过限制 using声明的范围,它的效力只能在该范围内持续; 当该范围结束时,它就被“撤消”了。

using声明在任何其他作用域之外的文件中声明时,它具有 file-scope 并影响该文件中的所有内容。

对于头文件,如果 using声明位于 file-scope,那么这将扩展到包含头文件的任何文件的作用域。