为什么"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个回答

6
为了回答你的问题,我从实际角度来看待它:很多程序员(并非全部)调用命名空间std。因此,我们应该养成不使用与命名空间std中相同名称或者有冲突的内容的习惯。这是一项重要的工作,但相对于可能存在的许多连贯单词和伪名来说,其量级并不算太大。
我的意思是,真的……说“不要依赖它的存在”只会让你依赖它的不存在。你会不断地遇到借用代码片段后需要不断修复的问题。只需将你的用户定义和借用的内容保持在有限的范围内,并且非常节制地使用全局变量(老实说,全局变量几乎总是“先编译,以后再考虑”时的最后手段)。我认为这是你的老师给出的错误建议,因为使用std既适用于“cout”,也适用于“std::cout”,但不使用std仅适用于“std::cout”。你不总能有幸编写所有自己的代码。
注意:在学习一些编译器工作原理之前,不要过于关注效率问题。通过一些编码经验,你可以在了解不多的情况下意识到编译器能够将好的代码泛化成简单的东西。就像你用C编写整个程序一样简单。好的代码只需要尽可能简单。

2
考虑到许多人似乎不知道有用的标准库函数(例如重新发明从<algorithm>中获取的函数),因此想象这些人可靠地避免使用这些标识符有些牵强。 看看你自己的代码,告诉我你是否从未使用过变量或函数名为 countdistancelogdestroylaunchvisitbetasamplemessagesclamperasecopymodulusleft等等。更不用说所有尚未在C++35发布时添加到std中的标识符,这些标识符会破坏你的代码...... - Toby Speight

5

为什么应避免使用 using namespace std;

原因 1:避免名称冲突。

C++ 标准库庞大且不断扩展,C++ 中的命名空间用于减少名称冲突。当你使用 "using namespace std;" 时,你会整体导入所有内容。

这就是为什么在任何专业代码中都不会出现 "using namespace std;"。

原因 2:编译失败。

因为它将 std 命名空间中定义的数百个内容(类、方法、常量、模板等)引入到全局命名空间中。并且不仅仅是对写有 "using namespace std" 的文件有效,还会递归地对包含该文件的任何文件有效。这很容易导致意外的ODR违规和难以调试的编译器/链接器错误。

示例:

当你在全局命名空间中声明函数 "max" 时,你使用了 std 命名空间。

由于没有使用 cmath 头文件,一切似乎工作正常。

当其他人包含你的文件并使用cmath头文件时,他们的代码意外地无法构建,因为全局命名空间中有两个名为"max"的函数。
原因3:可能在将来无法工作。
更糟糕的是,你无法预测将来会对std::命名空间进行哪些更改。这意味着今天有效的代码可能在以后停止工作,如果新添加的符号与你的代码中的某些内容冲突。
原因4:难以维护和调试。
使用namespace std;可能会产生难以维护和难以调试的代码。这是因为并不总是明显某些方面来自何处。开发人员可能指的是std::string类或一个唯一的字符串类,如果他们在没有更多解释的情况下使用术语"string"。

使用 namespace std 的代码

#include <iostream>
#include <algorithm>

using namespace std;

int swap = 0;

int main() {
    cout << swap << endl; // ERROR: reference to "swap" is ambiguous
}

没有 命名空间

#include <iostream>

int main() {
    using std::cout; // This only affects the current function

    cout << "Hello" <<'\n';
}

但是你可以使用 if,

  • 如果你想制作简短的教程或程序等,可以使用。

  • 如果你在源文件中频繁使用命名空间,并且确定不会发生冲突,那么使用 "using namespace std" 是没有问题的。


5
有一个非常简单的答案:这是一种防御性编程。你知道使用 std::size_tstd::cout等元素可以通过 using namespace std; 更加便捷 — 我希望你不需要说服,让这样的指令出现在头文件中毫无意义!然而,对于翻译单元内的情况,你可能会心动……
随着每个 C++ 版本的推出,属于 std 命名空间的类型、类等也在增加。如果您放松了对 std:: 的限定,潜在的歧义就太多了。对于经常使用的 std 中的名称,比如 using std::fprintf; 或更可能的是 using std::size_t;,放松限定符是完全合理的,但除非它们已经是语言(或特别是 C 库的 std 封装)的众所周知的部分,否则请使用限定符。
当您可以使用 typedef,结合 autodecltype 推断时,从可读性 / 可维护性的角度来看,实际上没有任何收益。

5
#include <iostream>

using namespace std;

int main() {
  // There used to be
  // int left, right;
  // But not anymore

  if (left != right)
    std::cout << "Excuse me, WHAT?!\n";
}

那么,为什么会这样呢?因为它“引入了与常用变量名重叠的标识符”,并让这段代码编译,将其解释为“如果(std::left != std::right)”。 PVS-Studio可以使用V1058诊断找到此类错误:https://godbolt.org/z/YZTwhp(感谢Andrey Karpov!!)。向cppcheck开发人员问声好:您可能希望标记此错误。这可不是玩笑。

4

是的,命名空间很重要。在我的项目中,有一次我需要将一个变量声明引入到我的源代码中,但编译时它与另一个第三方库发生了冲突。

最终,我不得不通过其他方式来解决这个问题,使代码变得不太清晰。


4

说实话,对我来说,这就像讨论缩进空格的数量一样无关紧要。

在头文件中使用指令会造成损害。但是在C++文件中呢?也许如果您同时使用两个命名空间。但是,如果只使用一个命名空间,它更多的是关于风格而不是真正的效率。

你知道为什么有关缩进的主题如此受欢迎吗?任何人都可以对其发表评论,并听起来非常聪明和有经验。


1
只需要一个例子就可以证明反驳:if (left != right) 在使用 using std::namespace; 编译时不需要在代码中存在任何 leftright。这是我在重构一个真实项目时遇到的一个真正的错误。std 中有很多常见的名称,这样的错误在许多商业代码库中仍然存在,其中原始值被分解,现在无效的用法仍然存在。这是一个 .cpp 源文件中的问题,而不是头文件中的问题。这不是一个观点问题。只要找到一个常见的例子,沉默的错误就会出现,游戏结束。禁止这种东西。 - Kuba hasn't forgotten Monica
公正的观点,我在回答时没有考虑到这一点。 - Timon Paßlick
在cpp文件中:您可能会惊讶于使用UnityBuilds时会发生什么......它们并不罕见... - Secundi

3
我认为在应用程序中使用“本地”或“全局”取决于应用场景。因为当我们将库文件用于本地时,有时代码会变得非常混乱,可读性下降。所以,我们只有在存在冲突可能性时才应该将库文件用于本地。我不是很有经验,如有错误请指出。

3

命名空间是为了避免命名冲突。C++ 基于 C,而 C 在函数和变量名称方面存在很多问题,因为有时来自不同库的函数会发生冲突。因此,库开发人员开始使用库名称作为前缀来定义它们的函数,例如以下示例:

foo/foo.h:

void libfoo_foo_foo_h_open(); // the name can be weird then even this one!

C++引入了命名空间来轻松解决这个问题。

假设您有两个库,一个叫做file,处理文件,另一个叫做window,处理窗口,并且以下代码:

#include <file.h>
#include <window.h>

using namespace file;
using namespace window;

void open() {
     ...
}

file.h:

namespace file {
    void open(); // What!
}

window.h:

namespace window {
    void open(); // Oh no!
}

上述代码肯定会编译失败。

如果你不喜欢输入 std:: (只有 5 个字符),你总是可以这样做:(不适用于头文件)

using s = std;

如果你仍然想在源文件中使用using namespace std;,那么你就是在邀请问题的发生,并且我必须问你一个问题:“名称空间的目的是什么?”

2
这里有一个例子,展示了如何使用using namespace std;可能会导致名称冲突的问题: 无法在C++中定义全局变量 在这个例子中,一个非常通用的算法名(std::count)与一个非常合理的变量名(count)发生了名称冲突。

1

为什么使用命名空间Std?

C++有一个标准库,其中包含在构建应用程序时使用的常见功能,如容器、算法等。如果这些名称是公开的,例如,如果它们在全局范围内定义了一个队列类,那么您将永远无法再次使用相同的名称而不会发生冲突。因此,他们创建了一个命名空间std来包含这种变化。

不使用的原因1: 不良实践

通常认为使用语句using namespace std是不良实践。替代这个声明的方法是每次我们声明一个类型时使用作用域运算符(::)指定标识符所属的命名空间。虽然该语句可以使我们在访问std命名空间中定义的类或类型时无需键入std::,但它会将整个std命名空间导入程序的当前命名空间。

不使用的原因2:编译器会混淆

在玩具程序中导入整个std库是可以的,但在生产级别的代码中,这是不好的。使用语句using namespace std; 会使std命名空间中声明的每个符号都可以在没有命名空间限定符的情况下访问。

例如:

现在,假设您升级到了新版本的C++,并且有更多新的std命名空间符号注入到您的程序中,而您对此一无所知。您可能已经在程序中使用了这些符号。现在编译器将很难确定声明的符号是属于您自己的实现还是从您导入的命名空间中而来。有些编译器会抛出错误。如果你运气不好,编译器选择了错误的实现并进行编译,那么肯定会导致运行时崩溃。
命名空间污染效应:
虽然这种做法对于示例代码来说还可以,但将整个std命名空间引入全局命名空间并不好,因为它违反了命名空间的目的,并可能导致名称冲突。这种情况被称为命名空间污染。

1
进一步阅读:https://dev.to/77bala7790/c-best-practice-1-don-t-simply-use-using-namespace-std-4n6b - Muhammad Ali

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