45

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

例如,

#include <iostream>

using namespace std;

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

#include <iostream>为什么仅仅拥有或仅仅拥有using namespace std并摆脱另一个是不够的?

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

4

11 回答 11

51

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

它所做的就是让编译器知道这些类型。仅包括<string>例如将使您能够参考std::string

#include <string>
#include <iostream>

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

现在,使用指令就像import在 Java 中一样。它们使名称在它们出现的范围内可见,因此您不必再完全限定它们。就像在 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

于 2008-12-23T21:30:24.380 回答
44

C++概念上是分开的。这是设计和有用的。

您可以包含没有名称空间会模棱两可的内容。

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

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

于 2008-12-23T20:10:42.217 回答
15

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

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

这来自 C++ 没有模块的概念。

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

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

//b.h
namespace eg {
    void bar();
}
于 2008-12-23T20:12:17.127 回答
8

包含定义功能的存在。

使用使它们更容易使用。

coutiostream 中定义的实际名称为“std::cout”。

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

std::cout << "Hello World";
于 2008-12-23T20:13:00.090 回答
4

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

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

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

于 2008-12-23T20:22:55.093 回答
4

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

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

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

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

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

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

于 2008-12-23T21:28:45.647 回答
4

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

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

于 2008-12-23T21:34:02.733 回答
3

如果你想真正理解这一点,你需要理解命名空间。

include你只包括头文件。

随着using namespace您声明您正在使用包含诸如 cout 之类的东西的给定名称空间。所以如果你这样做:

using namespace std;

你用cout你可以做

cout << "Namespaces are good Fun";

代替:

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

请注意,如果您不这样做,#include <iostream>您将无法在声明等中使用两者,因为您不包括标题std::coutcout

于 2008-12-23T20:13:25.627 回答
3

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

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

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

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

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

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

于 2008-12-23T22:20:55.743 回答
3

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

使用 std允许您省略std::前缀,但如果没有iostream则根本不能使用cout

于 2008-12-24T08:22:39.523 回答
2

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

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

假设您需要在 main 函数中定义一个向量,您这样做#include <vector>了,并且您的代码中现在有上面的代码:

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

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

于 2016-05-25T15:47:45.293 回答