C ++标头中的“使用命名空间”


119

在我们所有的C ++课程,所有的老师总是把using namespace std;在之后#include在它们的S .h文件。在我看来,这很危险,因为通过将标头包含在另一个程序中,我可能会将名称空间导入到我的程序中,而可能没有意识到,有意或不想要它(标头包含可以非常嵌套)。

所以我的问题是双重的:我是对的,using namespace不应该在头文件中使用它,并且/或者有某种方法可以撤消它,例如:

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

同样,还有一个问题:标头文件应该是#include其对应.cpp文件所需的所有标头,还是标头定义所需的所有标头,然后让.cpp文件#include剩下的标头,或者什么都不需要,并将其需要的所有内容声明为extern
问题背后的原因与上面的相同:包含.h文件时,我不希望感到惊讶。

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

谢谢。



3
作为附带说明,如果由于using namespace语句而导致名称冲突,则可以使用完全限定的名称来解决问题。
Marius Bancila

Answers:


115

绝对不要using namespace出于您所说的确切原因在标头中使用它,它可能会意外更改包含该标头的任何其他文件中的代码含义。无法撤消a using namespace,这是它如此危险的另一个原因。我通常只是使用grep或类似方法来确保using namespace不会在标头中调用它,而不是尝试更复杂的方法。也许静态代码检查器也会对此进行标记。

标头应仅包括它需要编译的标头。强制执行此操作的一种简单方法是始终将每个源文件自己的标头包括在其他标头之前。如果标头不是自包含的,那么源文件将无法编译。在某些情况下,例如,在库中引用实现详细信息类,则可以使用正向声明,而不是#include因为您可以完全控制此类正向声明的类的定义。

我不确定我是否会称其为通用,但它肯定会偶尔出现,通常是由不了解负面影响的新程序员编写的。通常,只需对风险进行一点了解就可以解决所有问题,因为它相对容易解决。


2
我们可以自由使用文件中的using语句.cpp吗?在3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators为死亡的指尖。
Christopher

1
以及我们应该如何简化template功能-应该位于标头中?typedefs
Christopher

1
@donlan,似乎您已经很长时间没有任何响应了……是的,您可以using.cpp文件中使用语句而不必担心,因为范围将仅限于该文件,但决不要在#include语句之前执行。至于头文件中定义的模板函数,不幸的是,我不知道有什么好的解决方案,只不过是写出名称空间...也许您可以将using声明放在单独的范围内{ /* using statement in between brackets */ },至少可以防止它转义当前文件。 。
tjwrona1992

26

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

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

命名空间using是为了您的方便,而不是您要强加给他人:切勿usingusing指令之前编写声明或#include指令。

结论:在头文件中,请勿编写名称空间级别的using指令或using声明;相反,显式命名空间限定所有名称。

头文件是一个或多个源文件中的访客。包含using指令和声明的头文件也使它的粗俗伙伴也随之而来。

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

更严重的是,我们有名称空间来避免名称冲突。头文件旨在提供接口。大多数标头都不知道现在或将来可能包含哪些代码。using在头内部添加方便内部使用的语句会将那些方便的名称放在该头的所有潜在客户端上。这可能导致名称冲突。这只是无礼。


12

将标头包含在标头中时,需要小心。在大型项目中,它会创建一个非常纠结的依赖关系链,从而触发比实际需要的更大/更长的重建。查看本文及其后续文章,以了解有关C ++项目中良好物理结构的重要性的更多信息。

仅在绝对需要时(无论何时需要类的完整定义),才应在标头中包含标头,并在任何可能的地方(当需要类是指针或引用时)都使用前向声明。

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


1
您如何简化template函数声明?必须在标题中出现,不是吗?
Christopher

6

查看Goddard太空飞行中心的编码标准(适用于C和C ++)。事实证明,这比以前要难一些-请参阅SO问题的最新答案:

GSFC C ++编码标准说:

§3.3.7每个头文件应#include是其需要编译的文件,而不是强迫用户#include使用所需的文件。#includes应限于头文件的需求;其他#includes应放置在源文件中。

现在,第一个交叉引用的问题包括GSFC C编码标准的引文和基本原理,但最终内容是相同的。


5

您认为using namespace标题中的内容很危险。我不知道如何撤消它。很容易检测到它,但是只需using namespace在头文件中搜索即可。出于最后一个原因,它在实际项目中并不常见。如果有人做了类似的事情,更有经验的同事很快就会抱怨。

在实际的项目中,人们会尽量减少所包含文件的数量,因为包含的文件越少,编译速度就越快。这样可以节省每个人的时间。但是,如果头文件假定在其之前应包含一些内容,则它应包含其本身。否则,它将使标头不独立。


4

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


4

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

只要您在整个项目范围内做出决定(“我们的项目广泛使用STL,并且我们不想在所有内容前都加上std ::。”),我看不出问题所在。毕竟,您唯一要冒的风险是名称冲突,并且由于STL的普遍存在,这不太可能成为问题。

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


4

关于“是否有某种方法可以撤消[ 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声明位于文件范围内,则它将扩展到包含头的任何文件的范围。


2
您似乎是唯一了解实际问题的人……但是,我在类减速中使用我的编译对我不是很满意。
rustypaper

通过解释OP关于范围应如何工作(如namespace声明内容)与实际如何工作(如变量)的思想,可以使这个答案更好。 在与它无关时{},限制它的范围{}。这是将其using namespace全局应用的偶然方式。
TafT

2

我相信,如果您在这样的嵌套名称空间中编写声明,则可以安全地在C ++头中使用“使用”:

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编译器上对其进行了测试。


这是我以前从未见过的有用技术。谢谢。通常,我会使用完整范围限定,并且将using声明放在函数定义的内部,这样我就不会对函数外部的命名空间造成污染。但是现在我想在头文件中使用C ++ 11用户定义的文字,并且按照通常的惯例,文字运算符受名称空间保护;但我不会在构造函数初始化程序列表中使用它们,而这些构造函数不在我可以使用非污染using声明的范围内。因此,这对于解决该问题非常有用。
安东尼·霍尔

尽管此模式的不幸副作用是,最内部名称空间内声明的任何类都将在编译器错误消息中显示为具有完全限定名称的:error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName ...。至少,这就是我在g ++中发生的事情。
安东尼·霍尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.