首页 文章

为什么“使用命名空间std”被认为是不好的做法?

提问于
浏览
2185

其他人告诉我,在代码中编写 using namespace std 是错误的,我应该直接使用 std::coutstd::cin .

为什么 using namespace std 被认为是一种不好的做法?是否效率低或者是否存在声明模糊变量(与 std 命名空间中的函数具有相同名称的变量)的风险?它会影响性能吗?

30 回答

  • 38

    有经验的程序员使用任何解决问题的方法,避免出现任何问题,并且出于这个原因,他们避免使用头文件级别的使用指令 .

    有经验的程序员也会尽量避免在源文件中对名称进行完全限定 . 造成这种情况的一个次要原因是,除非有充分的理由,否则在代码较少时编写更多代码并不优雅 . 这样做的一个主要原因是关闭依赖于参数的查找(ADL) .

    这些好理由是什么?有时程序员明确想要关闭ADL,有时他们想要消除歧义 .

    所以以下都可以:

    • 函数级实现中的函数级using指令和using声明

    • 源文件中的源文件级使用声明

    • (有时)源文件级使用指令

  • 332

    这一切都与管理复杂性有关 . 使用命名空间会拉出你不想要的东西,因此可能使调试变得更难(我说可能) . 在整个地方使用std ::更难阅读(更多文字和所有内容) .

    课程的马匹 - 管理你的复杂性,你最好能和最好的能力 .

  • 108

    根据我的经验,如果您有多个库使用say, cout ,但出于不同的目的,您可能使用错误的 cout .

    例如,如果我键入, using namespace std;using namespace otherlib; 并键入cout(恰好是两者),而不是 std::cout (或 'otherlib::cout' ),您可能使用了错误的一个,并且得到错误,它会更加有效和高效 . 使用 std::cout .

  • 35

    一个澄清问题的具体例子 . 想象一下,你有一个情况,你有2个库,foo和bar,每个库都有自己的命名空间:

    namespace foo {
        void a(float) { /* does something */ }
    }
    
    namespace bar {
        ...
    }
    

    现在让我们假设您在自己的程序中使用foo和bar,如下所示:

    using namespace foo;
    using namespace bar;
    
    void main() {
        a(42);
    }
    

    在这一点上一切都很好 . 当你运行程序时,它“做了一些事情” . 但是后来你更新吧,让我们说它已经变成了:

    namespace bar {
        void a(float) { /* does something completely different */ }
    }
    

    此时您将收到编译器错误:

    using namespace foo;
    using namespace bar;
    
    void main() {
        a(42);  // error: call to 'a' is ambiguous, should be foo::a(42)
    }
    

    所以你'll need to do some maintenance to clarify which ''你的意思(即 foo::a ) . 这可能是不可取的,但幸运的是它非常简单(只需在 a 的所有调用前添加 foo:: ,编译器标记为不明确) .

    但想象一下另一种方案,其中bar改为看起来像这样:

    namespace bar {
        void a(int) { /* does something completely different */ }
    }
    

    此时,您对 a(42) 的调用突然绑定到 bar::a 而不是 foo::a 而不是'something',它确实'something completely different' . 没有编译器警告或任何东西你的程序只是默默地开始做一些与以前完全不同的事情 .

    当您使用命名空间时,您会冒这样的情况,这就是人们使用命名空间感到不舒服的原因 . 命名空间中的事物越多,冲突的风险就越大,因此人们使用命名空间std(由于该命名空间中的事物数量)比其他命名空间更令人不舒服 .

    最终,这是可写性与可靠性/可维护性之间的权衡 . 可读性也可以考虑,但我可以看到这种论点的论点 . 通常我会说可靠性和可维护性更重要,但在这种情况下,您将不断支付可写性成本,以获得相当罕见的可靠性/可维护性影响 . “最佳”权衡取决于您的项目和优先事项 .

  • 9

    这是一种不好的做法,通常称为全局名称空间污染 . 当多个命名空间具有相同的带有签名的函数名时,可能会出现问题,然后编译器决定调用哪一个将是不明确的,当您使用函数调用 std::cout 指定命名空间时,这一切都可以避免 . 希望这可以帮助 . :)

  • 174

    使用非限定导入标识符,您需要外部搜索工具(如grep)来查找声明标识符的位置 . 这使得关于程序正确性的推理更加困难 .

  • 5

    我最近遇到了关于Visual Studio 2010的投诉 . 事实证明,几乎所有源文件都有这两行:

    using namespace std;
    using namespace boost;
    

    很多Boost功能都进入了C 0x标准,而Visual Studio 2010有很多C 0x功能,所以突然这些程序没有编译 .

    因此,避免 using namespace X; 是一种面向未来的形式,这种方法可以确保对正在使用的库和/或头文件的更改不会破坏程序 .

  • 11
    • 您需要能够阅读具有不同风格和最佳实践意见的人所编写的代码 .

    • 如果你只使用cout,没有人会感到困惑 . 但当你有很多名字空间飞来飞去,你看到这个类,你不确定它做了什么,明确命名空间作为一种评论 . 乍一看,你可以看到'哦,这是一个文件系统操作'或'那就是做网络的东西' .

  • 24

    我同意其他人的意见 - 它要求名称冲突,含糊不清,然后事实是它不那么明确 . 虽然我可以看到使用 using ,但我个人的偏好是限制它 . 我也会强烈考虑其他人指出的内容:

    如果你想找到一个可能是一个相当普通的名称的函数名,但是你只想在 std 名称空间中找到它(或者反过来 - 你想要更改所有不在名称空间 std 中的调用,名称空间 X ,.. . ),那你怎么建议这样做?您可以编写一个程序来执行此操作,但是花时间处理项目本身而不是编写程序来维护项目不是更好吗?

    就个人而言,我实际上并不介意 std:: 前缀 . 我喜欢这种外观而不是拥有它 . 我不知道这是不是因为它是明确的并且对我说"this isn't my code... I am using the standard library"或者它是否是别的,但我认为它看起来更好 . 这可能是奇怪的,因为我最近才进入C(使用并且仍然使用C和其他语言的时间更长,C是我有史以来最喜欢的语言,正好在汇编之上) .

    还有一件事虽然它与上述有些相关,但其他人指出了什么 . 虽然这可能是不好的做法,但我有时会为标准库版本和名称保留 std::name 以用于特定于程序的实现 . 是的确这可能会咬你并且咬你很难但这一切都归结为我从头开始这个项目而且我是唯一的程序员 . 示例:我重载 std::string 并将其命名为 string . 我有很多有用的补充 . 我之所以这样做是因为我的C和Unix(Linux)倾向于小写名称 .

    除此之外,您还可以拥有名称空间别名 . 以下是一个可能未被引用的示例 . 我使用C 11标准,特别是libstdc . 好吧,它没有完整的 std::regex 支持 . 当然它会编译,但它会引发异常,因为它是程序员的错误's end. But it is lack of implementation. So here'我是如何解决它的 . 安装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最终将拥有它 . 是的,开始您自己的项目并从一开始就使用标准(...)开始,这有助于维护,开发以及项目涉及的所有内容!

    Edit:
    现在我有时间,只是澄清一些事情 . 我不喜欢'String'的想法 . 事实上,我仍然偏向于C并且偏向于C . 保留细节,我工作的大部分内容更适合C(但这是一个很好的练习和一个很好的方法,使自己成为一个 . 学习另一种语言和b . 尽量不要偏向于对象/类/等等,这可能是更好的陈述不那么封闭,不那么傲慢,更容易接受 . 但是有用的是有些已经提出的建议:我确实使用list(它是相当通用的,不是吗?),排序(同样的事情)来命名两个如果我做 using namespace std; 会导致名称冲突等等最后我更喜欢具体,控制和知道,如果我打算将它作为标准用途,那么我将不得不指定它 . 简单地说:不假设允许 .

    至于使Boost的正则表达式成为 std 的一部分 . 我这样做是为了未来的整合 - 再次,我完全承认这是偏见 - 我不认为它像_101565那样丑陋 . 这对我来说确实是另一回事 . 在C语言中有许多东西我还没有完全接受外观和方法(另一个例子:可变参数模板与var args [虽然我承认可变参数模板非常有用!]) . 即使那些我接受它的人也很困难,我仍然遇到问题 .

  • 16

    考虑

    // myHeader.h
    #include <sstream>
    using namespace std;
    
    
    // someoneElses.cpp/h
    #include "myHeader.h"
    
    class stringstream {  // uh oh
    };
    

    请注意,这是一个简单的示例,如果您有包含20个包含和其他导入的文件,您将有大量的依赖关系来解决问题 . 更糟糕的是,根据冲突的定义,您可以在其他模块中获得无关的错误 .

    这并不可怕,但你不会在头文件或全局命名空间中使用它来避免头痛 . 在非常有限的范围内完成它可能是正常的,但我输入额外的5个字符以解释我的功能来自哪里我从未遇到过问题 .

  • 6

    为了回答你的问题,我实际上以这种方式看待它:许多程序员(不是全部)调用命名空间std . 因此,人们应该养成不使用那些东西的习惯使用与命名空间std中的名称相同或使用相同的名称 . 这是一个很大的问题,但与严格来说可能提出的可能连贯的单词和假名的数量相比,并没有那么多 .

    我的意思是真的......说“不要依赖于存在”只是让你依赖它不存在 . 你经常会有问题借用代码片段并不断修复它们 . 只需保留用户定义和借用的东西,因为它们应该是有限范围的,并且非常不使用全局变量(诚然,全局变量应该是“现在编译,后来理智”的最后手段) . 真的,我认为这是你老师的坏建议,因为使用std将同时适用于“cout”和“std :: cout”,但不使用std只适用于“std :: cout” . 你并不总是有幸写下你自己的代码 .

    注意:在实际了解编译器的工作原理之前,不要过分关注效率问题 . 通过一些编码经验,在您意识到他们能够将优秀代码概括为简单的东西之前,您不必了解它们 . 每一点都很简单,好像你用C编写了整个东西 . 好的代码只是它需要的复杂 .

  • 7

    “为什么'使用命名空间std;'被认为是C中的不良做法?“

    我反过来说:为什么输入5个额外的字符被一些人认为是麻烦的?

    考虑例如编写一个数字软件,当“向量”是问题域最重要的概念之一时,为什么我会考虑通过将一般的“std :: vector”切换为“向量”来污染我的全局命名空间?

  • 5

    我同意它不应该在全球范围内使用,但在本地使用并不是那么邪恶,就像 namespace 一样 . 这是"The C++ Programming Language"的一个例子:

    namespace My_lib {
    
        using namespace His_lib; // everything from His_lib
        using namespace Her_lib; // everything from Her_lib
    
        using His_lib::String; // resolve potential clash in favor of His_lib
        using Her_lib::Vector; // resolve potential clash in favor of Her_lib
    
    }
    

    在这个例子中,我们解决了潜在的名称冲突和由其组成引起的歧义 .

    在那里显式声明的名称(包括由 His_lib::String 等using声明声明的名称)优先于using-directive( using namespace Her_lib )在另一个作用域中可访问的名称 .

  • 6

    很高兴看到代码并知道它的作用 . 如果我看到 std::cout 我知道这是 std 库的 cout 流 . 如果我看到 cout 然后我不知道 . 它可能是 std 库的 cout 流 . 或者在同一个函数中可能会有10行高 . 或者在该文件中名为 coutstatic 变量 . 它可能是任何东西 .

    现在需要一个百万行代码库,这是不是在寻找一个bug,这意味着你知道这一百万行中有一行不能完成它应该做的事情 . cout << 1; 可以读取名为 coutstatic int ,将其向左移一位,然后扔掉结果 . 寻找一个bug,我必须检查一下 . 你能看出我真的更喜欢看 std::cout 吗?

    如果你是一名教师而且从来没有必要编写和维护任何代码,那么这些东西似乎是一个非常好的主意 . 我喜欢看代码,其中(1)我知道它的作用;并且,(2)我有信心写作它的人知道它做了什么 .

  • 19

    我同意Greg wrote的一切,但我想补充一下: It can even get worse than Greg said!

    Library Foo 2.0可以引入一个函数 Quux() ,这对于 Quux() 的一些调用比你多年来调用的 bar::Quux() 更明确地匹配 . 然后你的 code still compiles ,但 it silently calls the wrong function 并且天知道什么 . 事情就像事情一样糟糕 .

    请记住, std 命名空间有很多标识符,其中许多都是非常常见的(例如 listsortstringiterator 等),这些标识符也很可能出现在其他代码中 .

    如果你认为这不太可能:在Stack Overflow上有a question asked,在我给出这个答案后大约半年,这几乎发生了(错误的函数被称为由于省略了 std:: 前缀) . Here是这个问题的另一个最近的例子 . 所以这是一个真正的问题 .


    这里还有一个数据点:许多年前,我还常常发现,必须使用 std:: 标准库中的所有内容作为前缀 . 然后我在一个项目中工作,在开始时决定除了函数范围之外都禁止 using 指令和声明 . 你猜怎么着?我们大部分时间都花了很长时间才习惯编写前缀,经过几周之后,我们大多数人甚至同意它实际上使代码更具可读性 . 这是有原因的: Whether you like shorter or longer prose is subjective, but the prefixes objectively add clarity to the code. 不仅是编译器,而且你也发现更容易看到引用了哪个标识符 .

    十年来,该项目增长了数百万行代码 . 由于这些讨论一次又一次地出现,我曾经很好奇在项目中实际使用(允许)函数范围 using 的频率 . 我发现 std:: 发现 std:: 痛苦到足以使用指令甚至每100 kLoC甚至一次它被允许使用的地方 .


    底线:明确地为所有内容添加前缀不会造成任何伤害,很少习惯,并具有客观优势 . 特别是,它使编码器和人类读者更容易理解代码 - 这应该是编写代码时的主要目标 .

  • 4

    这取决于它的位置 . 如果它是一个公共头,那么你通过将它合并到全局命名空间来减少命名空间的值 . 请记住,这可能是制作模块全局变量的一种巧妙方法 .

  • 9

    命名空间是命名范围 . 命名空间用于对相关声明进行分组,并将单独的项目分开 . 例如,两个单独开发的库可能使用相同的名称来引用不同的项目,但用户仍然可以使用两者:

    namespace Mylib{
        template<class T> class Stack{ /* ... */ };
        / / ...
    }
    namespace Yourlib{
        class Stack{ /* ... */ };
        / / ...
    }
    void f(int max) {
        Mylib: :Stack<int> s1(max) ; / / use my stack
        Yourlib: :Stack s2(max) ; / / use your stack
        / / ...
    }
    

    重复命名空间名称可能会分散读者和作者的注意力 . 因此,可以声明来自特定命名空间的名称在没有明确限定的情况下可用 . 例如:

    void f(int max) {
        using namespace Mylib; / / make names from Mylib accessible
        Stack<int> s1(max) ; / / use my stack
        Yourlib: :Stack s2(max) ; / / use your stack
        / / ...
    }
    

    命名空间为管理不同的库和不同版本的代码提供了强大的工具 . 特别是,它们为程序员提供了如何明确地引用非本地名称的替代方案 .

    来源:Bjarne Stroustrup的C编程语言概述

  • 1201

    使用命名空间std的示例由于计数的模糊性而引发了编译错误,这也是算法库中的函数 .

    #include <iostream>
    
    using namespace std;
    
    int count = 1;
    int main() {
        cout<<count<<endl;
    }
    
  • 1846

    另一个原因是惊喜 .

    如果我看到 cout << blah ,而不是 std::cout << blah

    我想这是什么 cout ?这是正常的 cout 吗?这是特别的吗?

  • 11

    在所有条件下,我认为这不一定是不好的做法,但是在使用它时需要小心 . 如果您正在编写库,则可能应该使用范围分辨率运算符和命名空间来保持库与其他库的对接 . 对于应用程序级代码,我没有看到任何错误 .

  • 37

    简短版本:在C++ Coding Standards中,Herb Sutter和Andrei Alexandrescu不得不对此问题说些什么(重点强调是我的):

    摘要命名空间使用是为了您的方便,而不是为了对其他人造成:永远不要在#include指令之前编写using声明或using指令 . 推论:在头文件中,不要使用指令或使用声明来编写命名空间级别;相反,显式命名空间限定所有名称 . (第二条规则从第一条规则开始,因为 Headers 永远不会知道其他 Headers #includes可能会出现在它们之后 . )讨论简而言之:在#include指令和感觉之后,你可以而且应该在你的实现文件中使用声明和指令使用命名空间好的 . 尽管反复断言,使用声明和指令的命名空间并不是邪恶的,并且它们不会破坏命名空间的目的 . 相反,它们是命名空间可用的原因 .

  • 5

    using namespace 放在类的头文件中的问题在于它强制任何想要使用类的人(通过包含头文件)也是'using'(即看到所有内容)这些其他命名空间 .

    但是,您可以随意在(私有)* .cpp文件中放置using语句 .


    请注意,有些人不同意我的说法"feel free"这样 - 因为虽然cpp文件中的using语句比在头文件中更好(因为它仍然不好)(因为它依赖于代码它可以实现更难维护的课程 . )This FAQ topic说,

    using-directive存在于遗留C代码中,并且可以简化向命名空间的转换,但您可能不应该定期使用它,至少不应该在新的C代码中使用它 .

    FAQ提出了两种选择:

    • 使用声明:
    using std::cout; // a using-declaration lets you use cout without qualification
    cout << "Values:";
    
    • 只需输入std ::
    std::cout << "Values:";
    
  • 85

    这根本与性能无关 . 但请考虑一下:您正在使用两个名为Foo和Bar的库:

    using namespace foo;
    using namespace bar;
    

    一切正常,您可以从Foo调用 Blah() ,从Bar调用 Quux() 没有问题 . 但有一天你升级到新版本的Foo 2.0,它现在提供了一个名为 Quux() 的功能 . 现在你遇到了冲突:Foo 2.0和Bar都将 Quux() 导入你的全局命名空间 . 这将需要一些努力来修复,特别是如果函数参数碰巧匹配 .

    如果您使用过 foo::Blah()bar::Quux() ,则 foo::Quux() 的引入将是非事件 .

  • 15

    不应该在全局范围内使用using指令,尤其是在头文件中 . 但是,即使在头文件中也存在适当的情况:

    template <typename FloatType> inline
    FloatType compute_something(FloatType x)
    {
        using namespace std; //no problem since scope is limited
        return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
    }
    

    这比显式限定( std::sinstd::cos ...)更好,因为它更短并且能够使用用户定义的浮点类型(通过Argument Dependent Lookup) .

  • 72

    我也认为这是一种不好的做法 . 为什么?仅仅一天,我认为命名空间的功能是划分东西,所以我不应该_c封装东西,我写道: using std::cout; using std::cin; 在cpp文件中(从不在头文件中,因为它与_101507一起传播) . 我认为没有人会说出一个流 coutcin . ;)

  • 210

    它没有坏 . 包含 using namespace std 指令会根据您的需要以及您开发软件或项目的方式而有所不同 .

    namespace std 包含C标准函数和变量 . 当您经常使用C标准函数时,此命名空间很有用 .

    如本页所述:使用命名空间std的语句通常被认为是不好的做法 . 此语句的替代方法是每次声明类型时使用范围运算符(::)指定标识符所属的名称空间 . 并且看到这个观点:当您大量使用时,在源文件中使用“using namespace std”没有问题命名空间,并确保没有任何东西会碰撞 .

    有些人说在你的源文件中包含 using namespace std 是一种不好的做法,因为你从该命名空间调用了所有的函数和变量 . 当您想要定义一个与 namespace std 中包含的另一个函数同名的新函数时,您将重载该函数,并且由于编译或执行它可能会产生问题 . 它不会像你期望的那样编译或执行 .

    如本页所述:尽管该语句使我们无法在访问std命名空间中定义的类或类型时键入std ::但它会将整个std命名空间导入到程序的当前命名空间中 . 让我们举几个例子来理解为什么这可能不是一件好事......现在在开发的后期阶段,我们希望使用另一个版本的cout,它在一些名为“foo”的库中自定义实现(例如)...注意cout指向哪个库有歧义?编译器可能会检测到这一点,而不是编译程序 . 在最坏的情况下,程序仍然可以编译但调用错误的函数,因为我们从未指定标识符属于哪个命名空间 .

  • 17

    如果导入正确的头文件,则突然在全局范围内使用hexleftpluscount等名称 . 如果您不知道 std:: 包含这些名称,这可能会令人惊讶 . 如果你也尝试在本地使用这些名称,可能会引起一些混乱 .

    如果所有标准内容都在自己的命名空间中,则不必担心与代码或其他库的名称冲突 .

  • 5

    我同意这里的其他人,但是想解决有关可读性的问题 - 你可以通过在文件,函数或类声明的顶部使用typedef来避免所有这些问题 .

    我通常在我的类声明中使用它,因为类中的方法倾向于处理类似的数据类型(成员),而typedef是指定在类的上下文中有意义的名称的机会 . 这实际上有助于类方法定义的可读性 .

    //header
    class File
    {
       typedef std::vector<std::string> Lines;
       Lines ReadLines();
    }
    

    并在实施中:

    //cpp
    Lines File::ReadLines()
    {
        Lines lines;
        //get them...
        return lines;
    }
    

    而不是:

    //cpp
    vector<string> File::ReadLines()
    {
        vector<string> lines;
        //get them...
        return lines;
    }
    

    要么:

    //cpp
    std::vector<std::string> File::ReadLines()
    {
        std::vector<std::string> lines;
        //get them...
        return lines;
    }
    
  • 20

    不要在全球范围内使用它

    只有在 used globally 时才会被视为"bad" . 因为:

    • 你弄乱了你正在编程的命名空间 .

    • 当您使用多个 using namespace xyz 时,读者很难看到特定标识符的来源 .

    • 对于其他读者而言,对于最常见的读者来说,对于其他读者来说更是如此:你自己 . 一两年后回来看看......

    • 如果你只是谈论 using namespace std ,你可能不知道你抓到的所有东西 - 当你添加另一个 #include 或移动到新的C版本时,你可能会得到你不知道的名称冲突 .

    您可以在本地使用它

    继续在本地(几乎)自由使用它 . 当然,这可以防止你重复 std:: - 重复也很糟糕 .

    本地使用它的习语

    在C 03中有一个成语 - 样板代码 - 用于为您的类实现 swap 函数 . 有人建议您实际使用本地 using namespace std - 或至少 using std::swap

    class Thing {
        int    value_;
        Child  child_;
    public:
        // ...
        friend void swap(Thing &a, Thing &b);
    };
    void swap(Thing &a, Thing &b) {
        using namespace std;      // make `std::swap` available
        // swap all members
        swap(a.value_, b.value_); // `std::stwap(int, int)`
        swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
    }
    

    这有以下魔力:

    • 编译器将为 value_ 选择 std::swap ,即 void std::swap(int, int) .

    • 如果实现了重载 void swap(Child&, Child&) ,编译器将选择它 .

    • 如果没有那个重载,编译器将使用 void std::swap(Child&,Child&) 并尝试最好地交换这些 .

    使用C 11,没有理由再使用这种模式 . std::swap 的实现已更改为找到潜在的重载并选择它 .

  • 6

    同时使用多个命名空间显然是一个灾难的处方,但是在我看来,使用JUST命名空间 std 并且只有命名空间 std 并不是什么大不了的事,因为重新定义只能由你的自己的代码......

    所以只需将它们的功能视为保留名称,如“int”或“class”,就是这样 .

    人们应该停止这么肛门 . 你的老师一直都是对的 . 只需使用ONE命名空间;这是首先使用命名空间的重点 . 您不应该同时使用多个 . 除非是你自己的 . 因此,重新定义不会发生 .

相关问题