C++头文件中的 "using namespace"

143
在我们的所有C++课程中,所有老师都会在他们的.h文件中的#include之后紧跟着使用using namespace std;。对我来说,这似乎是危险的,因为当我在另一个程序中包含该头文件时,我的程序将获得导入的命名空间,可能是无意识、有意或不想要的(头文件的包含可以非常深)。那么我的问题是:我是否正确认为在头文件中不应该使用 using namespace,并且是否有一种方法可以撤消它,类似于:
//header.h
using namespace std {
.
.
.
}

另外一个类似的问题:在头文件中应该 #include 需要的所有头文件,还是只包含用于头文件定义的头文件并让 .cpp 文件 #include 其余部分,或者不包含任何头文件并将需要的所有内容声明为 extern
这个问题背后的原因与上面相同:我不希望在包含 .h 文件时出现意外。

另外,如果我说得对的话,这是一个常见的错误吗?我的意思是,在实际编程和 "真正的" 项目中是否普遍存在这种问题。

谢谢。


3
当你使用 using namespace std; 语句时,你可以在代码中省略 std:: 前缀,这样就不需要在每次使用标准库的类型、函数或对象时都写出完整的命名空间。但是,建议不要在头文件中使用该语句,因为它会影响到包含该头文件的所有源代码文件。最好的做法是只在源文件中使用 using namespace std;,这样可以确保最小的命名空间污染。 - Richard Inglis
3
顺带一提,如果由于使用了命名空间语句而导致名称冲突,那么可以使用完全限定名称来解决问题。 - Marius Bancila
9个回答

140
你绝对不应该在头文件中使用using namespace,正如你所说的原因,它可能会意外地改变包含该头文件的任何其他文件代码的含义。而且没有办法撤销已经使用的using namespace,这是它非常危险的另一个原因。我通常只使用grep或类似工具来确保头文件中没有调用using namespace,而不是尝试更复杂的方法。可能静态代码检查器也会标记这一点。
头文件应该只包含它需要编译的头文件。一个简单的方法来强制执行这一点是,总是将每个源文件的自己的头文件作为第一件事情加入到头文件中,然后在任何其他头文件之前。然后如果头文件不是自包含的,源文件就会编译失败。在某些情况下,例如在库中引用实现细节类时,可以使用前向声明而不是#include,因为你完全控制这样的前向声明类的定义。
我不确定是否普遍存在这种问题,但它确实偶尔出现,通常是由新程序员编写的,他们不知道负面影响。通常只需要一点关于风险的教育就能解决任何问题,因为修复问题相对简单。

3
我们可以在.cpp文件中自由使用using语句吗?3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterator会让手指生疼。 - Chris
2
我们应该如何优化“模板”函数——它们应该在头文件中吗?使用“typedefs”? - Chris
1
@donlan,看起来你已经很久没有得到回应了...是的,你可以在.cpp文件中使用using语句而不用太担心,因为作用域将仅限于该文件,但永远不要在#include语句之前这样做。至于在头文件中定义模板函数,不幸的是我不知道有什么好的解决方案,除了写出命名空间...也许你可以在一个单独的作用域内放置一个using声明{ /* 在括号之间使用语句 */ },这至少可以防止它逃离当前文件。 - tjwrona1992

37

Sutter和Alexandrescu的 “C ++编程标准:101个规则,指南和最佳实践” 中的第59项:

59.不要在头文件或#include之前写入命名空间using。

命名空间使用是为了方便你自己而不是折磨别人:永远不要在#include指令之前写入using声明或using指令。

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

头文件是一个或多个源文件中的客人。包含using指令和声明的头文件也带来了它的朋友们。

一个using声明带来了一个朋友。using指令带来了命名空间中的所有朋友。你老师使用的"using namespace std;"是一个using指令。

更重要的是,我们有命名空间来避免名称冲突。头文件旨在提供接口。大多数头文件都无法预知可能在现在或未来包含它们的代码。在头文件内部添加using语句以方便使用,就会将这些方便的名称强加给该头文件的所有潜在客户。这可能导致名称冲突。这只是很不礼貌。


13

在包含头文件时,需要小心嵌套头文件。在大型项目中,这可能会创建一个非常混乱的依赖链,导致比实际需要更大/更长的重建。阅读本文其后续文章,了解在C++项目中拥有良好物理结构的重要性。

只有在绝对需要(每当需要类的完整定义时)时,才应在头文件中包含头文件,并在可能使用前向声明(当类是指针或引用时所需的类)。

至于命名空间,我倾向于在头文件中使用显式命名空间作用域,并仅在cpp文件中使用using namespace


3
如何简化模板函数声明?它必须出现在头文件中,对吗? - Chris

11

关于“是否有某种方法可以撤消[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声明,则具有文件作用域并影响该文件中的所有内容。

对于头文件,如果using声明处于文件范围内,则将扩展到包含该头文件的任何文件的范围内。


5
你似乎是唯一一个理解实际问题的人...然而,我的编译器似乎不太满意我在类内部使用声明。 - rustypaper
这个答案可以更好地解释OP对作用域应该如何工作的想法(比如namespace声明)与它实际上是如何工作的(像一个变量)。用{}包围它限制了它的范围,而在它后面的{}与它无关。这是using namespace被全局应用的一种意外方式。 - TafT

7
请查阅戈达德航天局的编码标准(C和C ++)。这比以前更难了 - 请参见SO问题的更新答案:
• {{link1:我应该在头文件中使用#include吗}} • {{link2:C和C ++中自给自足的标题}}
GSFC C ++编码标准指出:
§3.3.7每个头文件都应该#include它需要编译的文件,而不是强制用户#include所需文件。#includes应限于头文件所需的内容;其他#includes应放置在源文件中。
交叉引用问题中的第一个现在包括来自GSFC C编码标准的引文和基本原理,但实质上最终相同。

5
您说的没错,头文件中使用using namespace是很危险的。我不知道如何撤销它。但是,很容易检测到它,只需在头文件中搜索using namespace即可。因此,在实际项目中很少见。更有经验的同事会很快抱怨某人这样做。在实际项目中,人们试图将包含的文件数量最小化,因为包含的文件越少,编译速度就越快,这样可以节省每个人的时间。然而,如果头文件假定在它之前应该包含某些内容,则应该自己包含它。否则,头文件就不是自包含的。

4

我相信如果你在嵌套命名空间中编写声明,就可以安全地在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;

这应该只包括在“DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED”中声明的内容,不包括使用的命名空间。我已经在mingw64编译器上测试过了。


2
这是一个我之前没有见过的有用技巧,谢谢。通常情况下,我使用完全限定作用域,并将 using 声明放在函数定义内部,以便它们不会污染函数外部的命名空间。但现在我想在头文件中使用 C++11 用户定义字面量,并且按照惯例,字面量运算符受到命名空间的保护;但是我想在构造函数初始化列表中使用它们,而该列表不在我可以使用非污染 using 声明的作用域内。因此,这对于解决这个问题非常有帮助。 - Anthony Hall
尽管这种模式的不幸副作用是,声明在最内层命名空间中的任何类都将以完全限定名称显示在编译器错误消息中:error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName ...。至少,在我的 g++ 中是这样的。 - Anthony Hall

4

你说得对。每个文件只应包含该文件所需的标头。至于“在实际项目中做错事情是否常见?”- 哦,是的!


4

在编程中,我认为实用主义应该胜过教条主义。

只要你做出项目范围内的决定(“我们的项目广泛使用STL,我们不想将所有东西都以std::为前缀。”),我认为这样做没有问题。毕竟,你所冒的风险只是名称冲突,在STL的普及下,这不太可能成为一个问题。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接