为什么"using namespace std;"被认为是不好的编程实践?

3347

我听说using namespace std;是不好的做法,应该直接使用std::coutstd::cin。这是为什么呢?是否会冒险声明与 std 命名空间中某些东西同名的变量?


719
不要忘记可以使用 "using std::cout;",这意味着您不必每次都输入 std::cout,但同时不要引入整个 std 命名空间。 - Bill
120
在头文件的文件范围内使用“using namespace std”特别不好。在源文件(*.cpp)的文件范围内,在所有包含文件之后使用它并不那么糟糕,因为其影响仅限于单个翻译单位。在函数或类中使用它甚至更少问题,因为其影响仅限于函数或类的作用域。 - sh-
15
我不推荐使用 using 命令,除非针对特定的命名空间,例如 std::literals::chrono_literalsPoco::Data:KeywordsPoco::Units 等与字面值或可读性技巧有关的内容。无论是在头文件还是实现文件中都不应该使用。在函数作用域内可能还可以接受,但除了字面值和相关内容外都没有用处。 - Ludovic Zenohate Lagouardette
22
@Jon:这与特定的命名空间std无关。我强调的重点是“在头文件中的文件作用域”。简言之,不要在头文件的文件作用域中使用“using namespace”(包括std或其他)。在实现文件中使用是可以的。抱歉造成的歧义。 - sh-
16
这只是在头文件中被认为是不好的编程习惯,在未被包含在其他地方的源文件(即cpp文件)中则可以。请参见@mattnewport下面的答案。https://dev59.com/D3M_5IYBdhLWcg3wQQ3w#26722134 - Danra
显示剩余4条评论
42个回答

13

一个示例,在该示例中using namespace std由于算法库中也存在函数count的歧义而导致编译错误。

#include <iostream>
#include <algorithm>

using namespace std;

int count = 1;
int main() {
    cout << count << endl;
}

3
::count--问题已解决。通常情况下,从std命名空间中获取的内容比其他地方多,因此保留using namespace指令可能会节省您的打字时间。 - Petr Skocik
这里真正的问题是C++仍然有没有命名空间的全局变量。这个问题以及方法中'this'是隐式的,导致了很多错误和问题,即使使用正确的“计数”变量,我也无法计算它们的数量。 ;) - Aiken Drum

11
这是具体情况具体分析。我们希望在软件寿命周期中尽量减少“总拥有成本”。使用“using namespace std”也有一些成本,但是不使用它对易读性的影响也是一个成本。
人们正确地指出,当您在使用它时,当标准库引入新的符号和定义时,您的代码将停止编译,并且您可能被迫重命名变量。然而,这可能很好,因为未来维护人员如果您正在使用关键字进行某些令人惊讶的用途,他们可能会短暂地感到困惑或分心。
您不希望拥有一个名为vector的模板(例如),它不是其他所有人都知道的vector。在C++库中引入的新定义数量足够小,可能不会出现这种情况。必须进行此类更改存在成本,但成本并不高,并且可通过不使用std符号名称来获得清晰度。
鉴于类、变量和函数的数量,对每个变量都声明std::可能会将您的代码膨胀50%,并使其更难以理解。现在,可以在一个屏幕的代码中完成算法或方法中的步骤,需反复上下滚动才能跟进。这是一个真正的成本。可以说这可能不是一个高成本,但否认它的人缺乏经验、教条或者简单地错误。
我提供以下规则:
1. std与其他所有库不同。这是每个人基本上需要知道的库,我认为最好将其视为语言的一部分。通常,即使对于其他库没有理由,使用using namespace std也是非常好的。
2. 永远不要通过在头文件中加入此using来强制决策编译单元(.cpp文件)的作者。始终将决策推迟到编译单元的作者身上。即使在已决定在整个项目中都使用using namespace std的项目中,也可以发现有一些最好作为该规则的异常处理的模块。
3. 即使使用命名空间功能允许您具有许多使用相同定义的模块,这样做可能会引起混淆。尽量保持名称不同。即使不使用命名空间功能,如果您有一个名为foo的类,而std引入了一个名为foo的类,从长远来看,重命名您的类可能更好。
4.使用命名空间的另一种选择是手动给符号加上前缀来进行命名空间管理。我有两个库已经用了数十年,它们起初是C库,实际上每一个符号都以"AK"或"SCWin"为前缀。一般而言,这就像避免使用 "using" 语句,但是你不需要写双冒号。"AK::foo()" 相当于 "AKFoo()"。这样可以使代码更加紧凑和简洁,代码量可以减少5-10%,唯一的缺点是如果你必须同时使用两个具有相同前缀的库时,可能会遇到大麻烦。请注意,在这方面,X Window库表现得非常好,只是他们在一些 #define 中忘记做到这一点:TRUE 和 FALSE 应该被命名为 XTRUE 和 XFALSE,并且这引起了与 Sybase 或 Oracle 的命名空间冲突,这两者也使用了带有不同值的 TRUE 和 FALSE!(在这种情况下是ASCII 0和1!)这种方法的一个特殊优点是,它不仅适用于预处理器定义,而且C++的 using/namespace 系统无法处理它们。这样做的一个好处是,它为从项目到最终成为库提供了一个有机的斜坡。在我的一个大型应用程序中,所有窗口类都以 "Win" 为前缀,所有信号处理模块 Mod 等等。这些很少有机会被重复使用,所以将每个组转换成一个库没有实际的好处,但是它可以在几秒钟内清楚地显示出项目是如何分成子项目的。

1
最后,感谢!相对于使用标准库“或许”修复遗留代码所需的时间,在编写每个代码时节省时间。 - Ingo Mi

11
情况下,运行结果可能会出现意外的错误。

在你的源代码中加入命名空间并不会使你的软件或项目性能变差。包含 using namespace std 指令取决于你的需求和你开发软件或项目的方式。 namespace std 包含了 C++ 标准函数和变量,当你经常使用 C++ 标准函数时这个命名空间是很有用的。

如此页面所述:

using namespace std 语句通常被认为是不好的实践。替代这个语句的方法是每次声明类型时使用作用域操作符(::)来指定标识符所属的命名空间。

还可以看看这个观点

如果你经常使用该命名空间,并确定不会发生冲突,则在源文件中使用 "using namespace std" 没有问题。

有些人说在你的源文件中包含 using namespace std 是不好的实践,因为你会从该命名空间中调用所有函数和变量。当你想要定义一个与 namespace std 中另一个函数同名的新函数时,你会重载函数并可能会导致编译或执行问题。它不会按照你的期望编译或运行。

如此页面所述:

尽管这个语句节约了我们在访问 std 命名空间中定义的类或类型时输入 std:: 的时间,但它会将整个 std 命名空间导入程序的当前命名空间中。接下来看几个例子,以了解为什么这可能不是一件好事。

...

现在在开发的后期,我们希望使用在名为“foo”的某个库中自定义实现的另一个版本的 cout(例如)

...

注意到有一个歧义,cout 指向哪个库?编译器可能会检测到这一点并不编译程序。在最坏的情况下,运行结果可能会出现意外的错误。

在某些情况下,程序可能仍然可以编译,但会调用错误的函数,因为我们从未指定标识符属于哪个命名空间。


8
我同意其他人的看法——这会导致名称冲突、歧义,而且事实上它不够明确。虽然我可以看到using的用处,但我个人更喜欢限制使用它。我也强烈考虑一些其他人指出的问题:
如果您想要查找一个可能是相当普通的函数名,但您只想在std命名空间中查找它(或反过来-您想要更改所有不在命名空间std、命名空间X等中的调用),那么您打算如何做?
您可以编写一个程序来完成这项工作,但是把时间花在项目本身上而不是编写一个维护项目的程序岂不更好?
就个人而言,我实际上并不介意std::前缀。我喜欢这种外观胜过没有它。我不知道是否因为它很明确,并告诉我“这不是我的代码...我正在使用标准库”,或者还有其他原因,但我认为它看起来更漂亮。这可能很奇怪,因为我最近才开始接触C++(长期使用和仍在使用C和其他语言,C是我最喜欢的语言之一,比汇编语言更好)。
还有一件事,虽然它与上面和其他人指出的问题有些相关。虽然这可能是不好的做法,但我有时会将std::name保留给标准库版本,而将name保留给特定于程序的实现。是的,确实可能会让你受到打击,但最终归结于我从头开始启动了这个项目,并且我是唯一的程序员。例如:我重载了std::string并将其称为string。我添加了一些有用的内容。我这样做部分原因是因为我倾向于使用小写名称的C和Unix(+ Linux)。
除此之外,您可以有命名空间别名。这是一个有用的例子,可能没有被提到过。我使用C++11标准,具体是libstdc++。嗯,它没有完整的std::regex支持。当然,它可以编译,但会抛出类似于程序员端错误的异常。但它缺乏实现。
所以这就是我的解决办法。安装Boost的正则表达式,并链接它。然后,我执行以下操作,以便在libstdc++完全实现它时,我只需要删除此块,代码仍然保持不变:
namespace std
{
    using boost::regex;
    using boost::regex_error;
    using boost::regex_replace;
    using boost::regex_search;
    using boost::regex_match;
    using boost::smatch;
    namespace regex_constants = boost::regex_constants;
}

我不会争论这是否是一个坏主意。但是我会争论它让我的项目更加清晰,同时使其更加具体:确实,我必须使用Boost,但是我正在像libstdc++最终将要用的那样使用它。是的,从标准库开始自己的项目并在一开始就使用它可以帮助维护、开发以及与项目相关的所有内容!

只是想澄清一些事情:我实际上并不认为在STL中故意使用类/其他名称是一个好主意,更具体地说是替代。字符串对我来说是个例外(忽略上面的第一个或第二个,如果你愿意),因为我不喜欢“String”的想法。

至于现在,我仍然非常偏爱C,偏见C ++。不多说,我所做的大部分工作都适合C(但这是一个很好的练习,也是让自己学会另一种语言,并尝试不那么偏见于对象/类等方面的好方法,这可能更好地表述为不那么心胸狭窄、不那么傲慢和更加接纳)。但是一些人已经提出了有用的建议:我确实使用list(它相当通用,不是吗?)和sort(同样的东西)来命名两个如果我要做using namespace std;就会引起名称冲突的东西,所以为了达到这个目的,我更喜欢具体、可控,并且知道如果我打算将其作为标准使用,则必须指定它。简而言之:不允许假设。

至于使Boost的regex成为std的一部分。我这样做是为了未来的集成 - 再次承认这完全是偏见 - 我认为它并不像boost::regex:: ...那么丑陋。事实上,对我来说还有另一个问题。在C ++中有很多事情我仍然没有完全接受外观和方法(另一个例子:变长模板与var参数[虽然我承认变长模板非常非常有用!])。即使是我接受的那些,也很困难,而且我仍然有问题。


1
“扩展std命名空间是未定义行为”,因此永远不应该这样做。 - tambre

8

根据我的经验,如果你有多个库使用相同的 cout,但是用途不同,你可能会使用错误的 cout

例如,如果我输入了 using namespace std;using namespace otherlib;,并只输入了 cout(它恰好存在于两个库中),而不是 std::cout (或 'otherlib::cout'),那么你可能会使用错误的一个,从而产生错误。使用 std::cout 更加有效和高效。


7

如果导入的标识符不合格,您需要外部搜索工具(例如grep)来查找标识符的声明位置。这使得关于程序正确性的推理更加困难。


7
为什么在C++中使用“using namespace std;”被认为是不良实践?
我想反过来问:为什么有些人认为输入五个额外字符很麻烦呢?
比如,考虑写数值软件。当“vector”是问题域中最重要的概念之一时,为什么要把通用的“std::vector”缩写成“vector”,从而污染全局命名空间呢?

23
这不仅仅是额外的5个字符;每当您引用标准库中的任何对象类型时,都会增加5个额外字符。如果您经常使用标准库,这将经常发生。因此,在一个相当大的程序中,实际上会有成千上万个额外的字符。可以假设“using”指令被添加到语言中,以便可以使用... - Jeremy Friesner
5
不是每次都要额外增加5个字符,而只需在你选择的编辑器中下拉菜单,进行查找和替换,可能需要点击鼠标几次。 - DaveWalley
1
可读性更好。cout << hex << setw(4) << i << endl;std::cout << std::hex << std::setw(4) << i << std::endl; 更易于阅读。 - oz1cz
18
甚至更糟糕的是:std::map<std::string,std::pair<std::string,std::string>>相比于map<string,pair<string,string>>非常糟糕。 - oz1cz
4
最好的做法是为你的STL容器进行typedef,这样std::就无关紧要了。而C++11引入了auto关键字,使得在使用迭代器时更加容易。 - juzzlin
1
同意@juzzlin的观点 - 最好的代码现在几乎不显示任何类型。您可以使用“auto”使编译器推断几乎所有内容。只有头文件真正需要指定,因为用于推断类型的代码通常不存在。 - Aiken Drum

7

我不认为在所有情况下都是不好的做法,但您需要小心使用。如果您正在编写一个库,您可能应该使用命名空间和作用域解析运算符来防止您的库与其他库产生冲突。对于应用程序级别的代码,我认为没有任何问题。


7

这是一种不良的做法,通常称为全局命名空间污染。当多个命名空间具有相同签名的函数名称时,可能会出现问题,此时编译器无法确定调用哪一个,但如果您在函数调用中指定命名空间,如std :: cout,则可以避免所有这些问题。希望这有所帮助。:)


6

这取决于它所在的位置。如果它是一个常见的头文件,那么把它合并到全局命名空间中会降低命名空间的价值。请记住,这可能是一种使模块全局变量化的好方法。


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