【问题标题】:C++ namespace and includeC++ 命名空间和包含
【发布时间】:2008-12-23 20:04:20
【问题描述】:

为什么我们在 C++ 程序中同时需要 using namespaceinclude 指令?

例如,

#include <iostream>

using namespace std;

int main() {
 cout << "Hello world";
}

为什么仅拥有#include &lt;iostream&gt; 或仅拥有using namespace std 并摆脱另一个还不够?

(我在想Java的类比,import java.net.* 将从java.net 导入所有内容,你不需要做任何其他事情。)

【问题讨论】:

    标签: c++ namespaces header-files


    【解决方案1】:

    using 指令和 include 预处理器指令是两个不同的东西。 include大致对应Java的CLASSPATH环境变量,或者java虚拟机的-cp选项。

    它的作用是让编译器知道类型。例如,仅包括&lt;string&gt; 就可以让您参考std::string

    #include <string>
    #include <iostream>
    
    int main() {
        std::cout << std::string("hello, i'm a string");
    }
    

    现在,使用指令就像 Java 中的 import。它们使名称在它们出现的范围内可见,因此您不必再完全限定它们。就像在 Java 中一样,必须知道使用的名称才能使其可见:

    #include <string> // CLASSPATH, or -cp
    #include <iostream>
    
    // without import in java you would have to type java.lang.String . 
    // note it happens that java has a special rule to import java.lang.* 
    // automatically. but that doesn't happen for other packages 
    // (java.net for example). But for simplicity, i'm just using java.lang here.
    using std::string; // import java.lang.String; 
    using namespace std; // import java.lang.*;
    
    int main() {
        cout << string("hello, i'm a string");
    }
    

    在头文件中使用 using 指令是一种不好的做法,因为这意味着碰巧包含它的每个其他源文件都会使用非限定名称查找来查看这些名称。与在 Java 中,您只使名称对出现导入行的包可见,而在 C++ 中,如果它们直接或间接包含该文件,它会影响整个程序。

    在全局范围内执行此操作时要小心,即使在实现文件中也是如此。最好尽可能在本地使用它们。对于命名空间 std,我从不使用它。我和许多其他人总是在名字前面写上std::。但如果你碰巧这样做,就这样做吧:

    #include <string>
    #include <iostream>
    
    int main() {
        using namespace std;
        cout << string("hello, i'm a string");
    }
    

    关于什么是命名空间以及为什么需要它们,请阅读 Bjarne Stroustrup 在 1993 年提出的将它们添加到即将到来的 C++ 标准中的提案。写的很好:

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/1993/N0262.pdf

    【讨论】:

    • 这比我上面的答案要好得多。
    • 这是迄今为止最实用的答案,并解释了为什么程序员应该关心(范围界定后果的 b/c)。这个答案应该被接受。
    【解决方案2】:

    C++ 中,概念是分开的。这是设计使然,而且很有用。

    您可以包含没有命名空间会产生歧义的内容。

    使用命名空间,您可以引用两个具有相同名称的不同类。当然,在这种情况下,您不会使用 using 指令,或者如果您这样做了,则必须在您想要的命名空间中指定其他东西的命名空间。

    还请注意,您不需要 using - 您可以使用 std::cout 或您需要访问的任何内容。您在项目前加上命名空间。

    【讨论】:

      【解决方案3】:

      在 C++ 中,#includeusing 具有不同的功能。

      #include 将包含文件的文本放入源文件(实际上是translation unit),另一方面,命名空间只是一种具有唯一名称的机制,以便不同的人可以创建一个“foo”对象。

      这是因为 C++ 没有模块的概念。

      请记住,C++ 中的命名空间是开放的,这意味着不同的文件可以定义同一命名空间的不同部分(有点像 .NET 的部分类)。

      //a.h
      namespace eg {
          void foo();
      }
      
      //b.h
      namespace eg {
          void bar();
      }
      

      【讨论】:

      • //a.h 和//b.h 的评论是关于什么的?
      • @RFV5s 它应该表明第一个 namespace eg 在一个名为 ah 的文件中,第二个在另一个名为 bh 的文件中即使它们位于两个不同的文件中,namespace eg 现在也包括 foobar
      • 附带问题:我是否认为最好先输入#include &lt;foo&gt;,然后输入namespace bar { ... },而不是namespace bar { #include &lt;foo&gt; ... }
      • @mercury0114 通常不在命名空间内包含文件。
      【解决方案4】:

      include 定义了函数的存在。

      使用使它们更容易使用。

      在 iostream 中定义的cout 实际上被命名为“std::cout”。

      你可以通过写来避免使用命名空间。

      std::cout << "Hello World";
      

      【讨论】:

        【解决方案5】:

        这些关键字用于不同的目的。

        using 关键字使命名空间中的名称可在当前声明区域中使用。它主要是为了方便,这样您就不必一直使用完全限定名称。这个page 详细解释了它。

        #include 语句是一个预处理器指令,它告诉预处理器处理指定文件的内容,就好像这些内容已经出现在源程序中指令出现的位置一样。也就是说,您可以将此语句视为将包含的文件复制到当前文件中。然后编译器会编译整个文件,就像您将所有代码都写在一个大文件中一样。

        【讨论】:

          【解决方案6】:

          正如所指出的,C++ 和 Java 是不同的语言,并且做的事情有些不同。此外,C++ 更像是一种“开玩笑的”语言,而 Java 更像是一种设计语言。

          虽然using namespace std; 不一定是个坏主意,但将它用于所有命名空间将消除全部好处。命名空间的存在使您可以编写模块而不用考虑与其他模块的名称冲突,并且using namespace this; using namespace that; 可能会产生歧义。

          【讨论】:

          • Java 最初是设计出来的(80 年代初的 C++ 是“设计的”),但我很难相信从今天开始的 Java 符合任何“设计”的定义。
          【解决方案7】:

          我认为其他答案稍微有点不妥。在所有 C++、Java 和 C# 中,using/import 是完全可选的。所以这没有什么不同。

          然后你必须做一些其他的事情来让代码在所有三个平台上都是可见的。

          在 C++ 中,您至少必须将其包含到当前的翻译单元中(对于向量、字符串等的许多实现来说已经足够了),通常您还必须向链接器添加一些内容,尽管有些库会自动执行此操作基于包含(例如,在 Windows 上构建时提升)。

          在 C# 中,您必须添加对其他程序集的引用。这会处理包含和链接设置的等价物。

          在 Java 中,您必须确保代码位于类路径中,例如将相关的 jar 添加到其中。

          因此,所有三个平台都需要非常相似的东西,using/import(方便)和实际链接解析(要求)在这三个平台上是相同的。

          【讨论】:

            【解决方案8】:

            如果你想真正了解这一点,你需要了解namespaces

            include 只是包含头文件。

            使用using namespace,您声明您正在使用一个给定的命名空间,其中包含诸如 cout 之类的内容。所以如果你这样做:

            using namespace std;
            

            你可以使用cout

            cout << "Namespaces are good Fun";
            

            代替:

            std::cout << "Namespaces are Awesome";
            

            请注意,如果您不使用#include &lt;iostream&gt;,您将无法在声明等中使用std::coutcout,因为您没有包含标题。

            【讨论】:

            • 不应该是,而不是吗?
            【解决方案9】:

            一个班轮(并不是说这是新事物:)):

            using std 允许你省略 std:: 前缀,但你不能使用 cout 没有 iostream.

            【讨论】:

              【解决方案10】:

              甚至 Stroustrup 也将#include 机制称为有点骇人听闻。然而,它确实使单独编译更容易(运送编译的库和头文件而不是所有源代码)。

              真正的问题是“为什么 C++ —— 在它已经拥有 #include 机制之后—— 添加命名空间?”

              我所知道的关于为什么#include 不够用的最好例子来自 Sun。显然,Sun 开发人员在他们的产品中遇到了一些问题,因为他们编写了一个 mktemp() 函数,该函数恰好与 mktemp() 函数具有相同的签名,该函数是通过一个文件包含的,而该文件本身是通过项目的标题包含的想要。

              当然这两个功能是不兼容的,一个不能代替另一个。另一方面,编译器和链接器在构建二进制文件时并没有意识到这一点,有时mktemp() 会调用一个函数,有时它会调用另一个函数,这取决于不同文件的编译或链接顺序。

              问题源于这样一个事实,即 C++ 最初与 C 兼容 - 并且基本上搭载在 C 之上。而 C 只有一个全局命名空间。 C++ 通过命名空间解决了这个问题——在与 C 不兼容的代码中。

              C# 和 Java (1) 都有命名空间机制(C# 中的 namespace,Java 中的 package),(2) 通常通过 IDE 开发,为开发人员处理对二进制文件的引用,以及 (3) 不要' 不允许独立的函数(类作用域类似于命名空间,并降低了污染全局命名空间的风险),因此它们对这个问题有不同的解决方案。但是,对于您正在调用的方法仍然可能存在一些歧义(例如,类继承的两个接口之间的名称冲突),在这种情况下,所有三种语言都需要程序员明确指定它们是哪种方法实际查找,通常是在父类/接口名称前面加上。

              【讨论】:

                【解决方案11】:

                在 C++ 中,include 指令将在预处理步骤中将头文件复制并粘贴到源代码中。需要注意的是,头文件通常包含在命名空间中声明的函数和类。例如,&lt;vector&gt; 标头可能类似于以下内容:

                namespace std {
                    template <class T, class Allocator = allocator<T> > class vector;
                    ...
                } 
                

                假设您需要在 main 函数中定义一个向量,您可以使用#include &lt;vector&gt;,并且您现在的代码中有上面的代码:

                namespace std {
                    template <class T, class Allocator = allocator<T> > class vector;
                    ...
                }
                int main(){
                   /*you want to use vector here*/
                }
                

                请注意,在您的代码中,vector 类仍位于 std 命名空间中。但是,您的 main 函数位于默认的 global 命名空间中,因此仅包含标头不会使矢量类在 global 命名空间中可见。您必须使用using 或使用std::vector 之类的前缀。

                【讨论】:

                  猜你喜欢
                  • 2011-02-08
                  • 2019-06-20
                  • 2010-11-21
                  • 1970-01-01
                  • 2011-11-03
                  • 2014-09-06
                  • 2014-08-26
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多